在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
基本解释C++11引入了多线程,同时也引入了一套内存模型。从而提供了比较完善的一套多线程体系。在单线程时代,一切都很简单。没有共享数据,没有乱序执行,所有的指令的执行都是按照预定的时间线。但是也正是因为这个强的同步关系,给CPU提供的优化程度也就相对低了很多。无法体现当今多核CPU的性能。因此需要弱化这个强的同步关系,来增加CPU的性能优化。
C++11提供了6种内存模型: 1 enum 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 } 原子类型的操作可以指定上述6种模型的中的一种,用来控制同步以及对执行序列的约束。从而也引起两个重要的问题: 1.哪些原子类型操作需要使用内存模型?
原子操作可分为3大类: 读操作:memory_order_acquire, memory_order_consume 未被列入分类的memory_order_relaxed没有定义任何同步语义和顺序一致性约束
执行序列约束C++11中有3种不同类型的同步语义和执行序列约束: 1. 顺序一致性(Sequential consistency):对应的内存模型是memory_order_seq_cst 2.请求-释放(Acquire-release):对应的内存模型是memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel 3.松散型(非严格约束。Relaxed):对应的内存模型是memory_order_relaxed
下面对上述3种约束做一个大概解释: Sequential consistency:指明的是在线程间,建立一个全局的执行序列 Acquire-release:在线程间的同一个原子变量的读和写操作上建立一个执行序列 Relaxed:只保证在同一个线程内,同一个原子变量的操作的执行序列不会被重排序(reorder),这种保证也称之为modification order consistency,但是其他线程看到的这些操作的执行序列式不同的。 还有一种consume模式,也就是std::memory_order_consume。这个模式主要是引入了原子变量的数据依赖。
代码解释Sequential consistencySequential consistency有两个特性: 示例代码: 1 std::string work; 2 std::atomic<bool> ready(false); 3 4 void consumer(){ 5 while(!ready.load()){} 6 std::cout<< work << std::endl; 7 } 8 9 void producer(){ 10 work= "done"; 11 ready=true; 12 } 1. work = "done" sequenced-before ready=true 推导出 work = "done" happens-before ready=true 同时因为happens-before关系具有传递性,所以上述代码的执行序列式: work = "done" happens-before ready = true happens-before while(!ready.load()){} happens-before std::cout<< work << std::endl
Acquire-release关键思想是:在同一个原子变量的release操作和acquire操作间同步,同时也就建立起了执行序列约束。 所有的读和写动作不能移动到acquire操作之前。 release-acquire操作在线程间建立了一种happens-before。所以acquire之后的操作和release之前的操作就能进行同步。同时,release-acquire操作具有传递性。 示例代码: 1 std::vector<int> mySharedWork; 2 std::atomic<bool> dataProduced(false); 3 std::atomic<bool> dataConsumed(false); 4 5 void dataProducer(){ 6 mySharedWork={1,0,3}; 7 dataProduced.store(true, std::memory_order_release); 8 } 9 10 void deliveryBoy(){ 11 while( !dataProduced.load(std::memory_order_acquire) ); 12 dataConsumed.store(true,std::memory_order_release); 13 } 14 15 void dataConsumer(){ 16 while( !dataConsumed.load(std::memory_order_acquire) ); 17 mySharedWork[1]= 2; 18 } 1. mySharedWork={1,0,3}; is sequenced-before dataProduced.store(true, std::memory_order_release); 4. dataProduced.store(true, std::memory_order_release); is synchronizes-with while( !dataProduced.load(std::memory_order_acquire) ); 因此dataProducer和dataConsumer能够正确同步。
原子变量的数据依赖std::memory_order_consume说的是关于原子变量的数据依赖。
示例代码: 1 std::atomic<std::string*> ptr; 2 int data; 3 std::atomic<int> atoData; 4 5 void producer(){ 6 std::string* p = new std::string("C++11"); 7 data = 2011; 8 atoData.store(2014,std::memory_order_relaxed); 9 ptr.store(p, std::memory_order_release); 10 } 11 12 void consumer(){ 13 std::string* p2; 14 while (!(p2 = ptr.load(std::memory_order_consume))); 15 std::cout << "*p2: " << *p2 << std::endl; 16 std::cout << "data: " << data << std::endl; 17 std::cout << "atoData: " << atoData.load(std::memory_order_relaxed) << std::endl; 18 } 1. ptr.store(p, std::memory_order_release) is dependency-ordered-before while (!(p2 = ptr.load(std::memory_order_consume)))。因为后面的std::cout << "*p2: " << *p2 << std::endl;将读取load操作的结果。 综上所述,对于data和atoData的输出是没有保证的。因为它们和ptr.load操作没有carries-a-dependency-to关系。同时它们又不是原子变量,这将会导致race condition。因为在同一时间,多个线程可以访问data,线程t1(producer)同时会修改它。程序的行为因此是未定义的(undefined)。
参考: http://en.cppreference.com/w/cpp/atomic/memory_order |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论