什么是内存序 内存顺序是指在并发编程中, 对内存读写操作的执行顺序。这个顺序可以被编译器和处理器进行优化, 可能会与代码中的顺序不同, 这被称为指令重排。比如下面代码:
1 2 3 4 std::atomic<int > x{0 }, y{0 }; x.store (10 ); y.store (20 );
注意:虽然上面的变量x, y都是std::atomic类型,但是实际上程序在执行时可能是下面的情况:
1 2 y.store (20 ); x.store (10 );
也就是说,经过指令重排,实际上的执行顺序已经发生改变。
除此之外,现代CPU的多级缓存体系也会影响指令的执行。虽然MESI协议 保证了缓存一致性 ,但它只保证最终一致性 ,不保证实时性 。
C++的6种内存序 所谓内存序,实际上是指:对单个线程 内多个原子操作 效果可见性的一种顺序保证。
memory_order_seq_cst: 顺序一致性最强保证 。所有操作按照一个全局顺序执行,相当于在每个原子操作后面都加了内存屏障 。相对的,性能也会下降 。
1 2 3 4 5 6 7 8 9 10 11 std::atomic<int > x{0 }, y{0 }; x.store (1 , std::memory_order_seq_cst); y.store (1 , std::memory_order_seq_cst); if (y.load (std::memory_order_seq_cst) == 1 ) { assert (x.load (std::memory_order_seq_cst) == 1 ); }
memory_order_release与memory_order_acquire: 释放-获取语义这两个需要配对使用 。建立synchronizes-with关系(跨线程的同步关系)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 std::atomic<bool > ready{false }; int payload = 0 ;void publisher () { payload = 42 ; ready.store (true , std::memory_order_release); } void subscriber () { while (!ready.load (std::memory_order_acquire)) { std::cout << "ready is false, subscriber is waiting" << std::endl; } std::cout << payload << std::endl; }
注意:有时候又会造成误伤
请看下面代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 std::atomic<int > net_con{0 }; std::atomic<int > has_alloc{0 }; char buffer[1024 ];char file_content[1024 ];void release_thread (void ) { sprintf (buffer, "%s" , "something_to_read_tobuffer" ); net_con.store (1 , std::memory_order_release); has_alloc.store (1 , std::memory_order_release); } void acquire_thread (void ) { if (strstr (file_content, "auth_key =" )) { } while (!has_alloc.load (std::memory_order_acquire)); bool v = has_alloc.load (std::memory_order_acquire); if (v) { net_con.load (std::memory_order_relaxed); } }
从上面代码中可以看出,buffer与file_content的使用,与两个原子变量就目前的这段简短的代码而言是没有任何联系的。按理说,这两部分的代码是可以放到任何位置执行的。但是,由于使用了release-acquire,那么会导致的情况就是,buffer和file_content的访问都被波及。
memory_order_acq_rel: 获取-释放语义用于读-修改-写 操作,同时具有acquire和release语义。
1 2 3 4 5 6 7 8 9 std::atomic<int > counter{0 }; void increment () { counter.fetch_add (1 , std::memory_order_acq_rel); }
memory_order_relaxed: 松散排序 只保证原子性,不保证顺序 。性能最好,但最难正确使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <atomic> #include <iostream> #include <vector> #include <thread> std::atomic<int > counter{0 }; void increment_counter () { counter.fetch_add (1 , std::memory_order_relaxed); } std::atomic<bool > flag{false }; int data = 0 ;void dangerous_writer () { data = 42 ; flag.store (true , std::memory_order_relaxed); } void dangerous_reader () { while (!flag.load (std::memory_order_relaxed)) { printf ("data: %d\n" , data); } std::cout << "data: " << data << std::endl; } int main () { std::thread t2 (dangerous_reader) ; std::thread t1 (dangerous_writer) ; t1. join (); t2. join (); return 0 ; }