The std::memory_order
values allow you to specify fine-grained constraints on the memory ordering provided by your atomic operations. If you are modifying and accessing atomic variables from multiple threads, then passing the std::memory_order
values to your operations allow you to relax the constraints on the compiler and processor about the order in which the operations on those atomic variables become visible to other threads, and the synchronization effects those operations have on the non-atomic data in your application.
The default ordering of std::memory_order_seq_cst
is the most constrained, and provides the "intuitive" properties you might expect: if thread A stores some data and then sets an atomic flag using std::memory_order_seq_cst
, then if thread B sees the flag is set then it can see that data written by thread A. The other memory ordering values do not necessarily provide this guarantee, and must therefore be used very carefully.
The basic premise is: do not use anything other than std::memory_order_seq_cst
(the default) unless (a) you really really know what you are doing, and can prove that the relaxed usage is safe in all cases, and (b) your profiler demonstrates that the data structure and operations you are intending to use the relaxed orderings with are a bottleneck.
My book, C++ Concurrency in Action devotes a whole chapter (45 pages) to the details of the C++ memory model, atomic operations and the std::memory_order
constraints, and a further chapter (44 pages) to using atomic operations for synchronization in lock-free data structures, and the consequences of relaxed ordering constraints.
My blog entries on Dekker's algorithm and Peterson's algorithm for mutual exclusion demonstrate some of the issues.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…