A rough explanation of the C++11 memory model

Source: Internet
Author: User

Basic explanation

C++11 introduces multi-threading and also introduces a set of memory models. Thus provides a more complete set of multithreaded system. In the single-threaded era, everything was simple. No shared data, no disorderly execution, all instructions are executed according to the predetermined timeline. But it is also because of this strong synchronization relationship, the optimization of the CPU to provide a relatively low level. Does not reflect the performance of today's multicore CPUs. Therefore, this strong synchronization relationship needs to be weakened to increase CPU performance optimization.

C++11 provides 6 types of memory models:

  1enum memory_order{  2   memory_order_relaxed,  3   Memory_ Order_consume,  4   memory_order_acquire,  5   memory_order_release,   6   Memory_order_acq_rel,  7   memory_order_seq_cst  8 }

Atomic type operations can specify one of the above 6 models to control synchronization and constraints on the execution sequence. It also raises two important questions:

1. What atomic type operations need to use the memory model?
2. The memory model defines those synchronization semantics (synchronization) and execution sequence constraints (ordering constraints)?

Atomic operations can be divided into 3 main categories:

read operation:Memory_order_acquire, Memory_order_consume
write operation:memory_order_release
read-Modify-write operation:Memory_order_acq_rel, MEMORY_ORDER_SEQ_CST

Non-classified memory_order_relaxed does not define any synchronization semantics and sequential consistency constraints

To perform a sequence constraint

There are 3 different types of synchronization semantics and execution sequence constraints in c++11:

1. Sequential consistency (sequential consistency): The corresponding memory model is MEMORY_ORDER_SEQ_CST

2. Request-Release (Acquire-release): The corresponding memory model is memory_order_consume,memory_order_acquire,memory_order_release, Memory_order_acq_rel

3. Loose type (not strictly constrained. Relaxed): The corresponding memory model is memory_order_relaxed

Here is an approximate explanation of the above 3 constraints:

Sequential consistency: Indicates that a global execution sequence is established between threads

Acquire-release: Create an execution sequence on the read and write operations of the same atomic variable between threads

Relaxed: Only in the same thread, the execution sequence of the operation of the same atomic variable is not reordered (reorder), which is also known as the modification order consistency, but the execution sequence of these operations seen by other threads is different.

There is also a consume pattern, which is std::memory_order_consume. This pattern mainly introduces the data dependence of atomic variables.

Code explanation sequential Consistency

The sequential consistency has two properties:
1. The order in which all threads execute instructions is in the order of source code;
2. Each thread can see the same order in which the operations of other threads are executed.

Example code:

  1  std::string  work; 2  std::atomic<bool  > Ready (false );   4  void  consumer () { while  (!ready.load ()) {} std::cout<< work << Std::endl; } 8   9
         void  producer () {  work= "done  ";   ready=true ;  } 

1. work = "Done"   sequenced-before ready=true deduced work = "Done" happens-before ready=true< /strong>
2. while (!ready.load ()) {} sequenced-before std::cout<< work << Std::endl deduced while (!ready.load ()) {} happens-before std::cout<< work << Std::endl
3. ready = True synchronizes-with while (!ready.load ()) {} deduces ready = True while (!ready.load ()) {} , it is deduced that ready = True happens-before while (!ready.load ()) {}

At the same time, because the happens-before relationship has transitivity, the execution sequence of the above code is:

Work = ' Done ' happens-before ready = True happens-before while (!ready.load ()) {} Happens-before std::cout<< work << Std::endl

Acquire-release

The key idea is to synchronize the release operation of the same atomic variable with the acquire operation, and also establish the execution sequence constraints.

All read and write actions cannot be moved before the acquire operation.
All read and write actions cannot be moved after the release operation.

A happens-before is established between the Release-acquire operation threads. So the operation after acquire and the operation before release can be synchronized. At the same time, the Release-acquire operation has transitivity.

Example code:

1std::vector<int> mysharedwork;2std::atomic<BOOL> dataproduced (false);3std::atomic<BOOL> dataconsumed (false);4 5 voidDataproducer () {6mysharedwork={1,0,3};7Dataproduced.store (true, std::memory_order_release);8}9 Ten voidDeliveryboy () { One      while(!dataproduced.load (Std::memory_order_acquire)); ADataconsumed.store (true, std::memory_order_release); -} -  the voidDataconsumer () { -      while(!dataconsumed.load (Std::memory_order_acquire)); -Mysharedwork[1]= 2; -}

1. mysharedwork={1,0,3}; is sequenced-before Dataproduced.store (True, std::memory_order_release);
2. while (!dataproduced.load (Std::memory_order_acquire)), is sequenced-before Dataconsumed.store (true,std::memory_order_release);
3. while (!dataconsumed.load (Std::memory_order_acquire)), is sequenced-before mysharedwork[1] = 2;

4. Dataproduced.store (True, std::memory_order_release); is synchronizes-with while (! Dataproduced.load (Std::memory_order_acquire));
5. Dataconsumed.store (true,std::memory_order_release); is synchronizes-with while (! Dataconsumed.load (Std::memory_order_acquire));

So Dataproducer and Dataconsumer can synchronize correctly.

Data dependence of atomic variables

Std::memory_order_consume is talking about data dependencies on atomic variables.
There are two ways of data dependency:
1. Carries-a-dependency-to: If the result of operation A is used in the operation of Operation B, then a carries-a-dependency-to (bring the dependency into) b
2. Dependency-ordered-before: If the result of Operation B is further used by Operation C within the same thread, then A's stor operation (with Std::memory_order_release, Std::memory_ Order_acq_rel or STD::MEMORY_ORDER_SEQ_CST) is the load operation (with Std::memory_order_) of Dependency-ordered-before (before the dependent execution sequence x) b Consume).

Example code:

1STD::ATOMIC&LT;STD::string*> ptr;2 intData3std::atomic<int> atodata;4 5 voidProducer () {6STD::string* p =NewSTD::string("c++11");7data = 2011;8Atodata.store (2014,std::memory_order_relaxed);9Ptr.store (P, std::memory_order_release);Ten} One  A voidConsumer () { -STD::string* P2; -      while(! (P2 = ptr.load (std::memory_order_consume))); theStd::cout << "*P2:"<< *p2 << Std::endl; -Std::cout << "Data:"<< data << Std::endl; -Std::cout << "Atodata:"<< atodata.load (std::memory_order_relaxed) << Std::endl; -}

1. Ptr.store (P, std::memory_order_release) is dependency-ordered-before while (!) ( P2 = ptr.load (std::memory_order_consume)). Because the back of the std::cout << "*P2:" << *p2 << Std::endl; The result of the load operation is read.
2. While (!) ( P2 = ptr.load (std::memory_order_consume)) carries-a-dependency-to std::cout << "*P2:" < < *P2 << Std::endl. Because the output of the *P2 uses the results of the ptr.load operation

In summary, there is no guarantee for the output of data and atodata. Because they have no carries-a-dependency-to relationship with ptr.load operations. At the same time they are not atomic variables, which will result in race condition. Because at the same time, multiple threads can access data, and thread T1 (producer) modifies it. The behavior of the program is therefore undefined (undefined).

Reference:

Http://en.cppreference.com/w/cpp/atomic/memory_order
http://www.modernescpp.com/

A rough explanation of the C++11 memory model

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.