走进C++11(四十)最宽松的顺序 memory_order_relaxed 内存模型(三)

关注公众号获取更多信息:

 

之前讲的都是理论相关的,下面详细讲一下我们现实中会使用到的内存模型。今天是最简单也是最宽松的内存模型----memory_order_relaxed 

 

带标签 memory_order_relaxed 的原子操作无同步操作;它们不会在同时的内存访问间强加顺序。它们只保证原子性和修改顺序一致性。

例如,

 

对于最初为零的 x 和 y ,// 线程 1 :r1 = y.load(std::memory_order_relaxed); // Ax.store(r1, std::memory_order_relaxed); // B// 线程 2 :r2 = x.load(std::memory_order_relaxed); // Cy.store(42, std::memory_order_relaxed); // D

 

 

 

 

允许产生结果 r1 == 42 && r2 == 42 ,因为即使线程 1 中 A 先序于 B 且线程 2 中 C 先序于 D ,却没有制约避免 y 的修改顺序中 D 先出现于 A ,而 x 的修改顺序中 B 先出现于 C 。D 在 y 上的副效应,可能可见于线程 1 中的加载 A ,同时 B 在 x 上的副效应,可能可见于线程 2 中的加载 C 。

 

即使使用宽松内存模型,也不允许“无中生有”的值循环地依赖于其各自的计算,例如,对于最初为零的 x 和 y ,

// 线程1:
r1 = x.load(std::memory_order_relaxed);
if (r1 == 42) y.store(r1, std::memory_order_relaxed);
// 线程2:
r2 = y.load(memory_order_relaxed);
if (r2 == 42) x.store(42, std::memory_order_relaxed);

不允许产生结果 r1 == 42 && r2 == 42 ,因为存储 42 于 y 只在存储 42 于 x 后有可能,这又循环依赖于存储 42 于 y 。注意 C++14 前规范曾在技术上允许,但不推荐实现者如此实现。

(C++14 起)

 

宽松内存顺序的典型使用是计数器自增,例如 std::shared_ptr 的引用计数器,因为这只要求原子性,但不要求顺序或同步(注意 std::shared_ptr 计数器自减要求与析构函数进行获得释放同步)

 

下面举个例子:

 

#include <vector>#include <iostream>#include <thread>#include <atomic> std::atomic<int> cnt = {0}; void f(){    for (int n = 0; n < 1000; ++n) {        cnt.fetch_add(1, std::memory_order_relaxed);    }} int main(){    std::vector<std::thread> v;    for (int n = 0; n < 10; ++n) {        v.emplace_back(f);    }    for (auto& t : v) {        t.join();    }    std::cout << "Final counter value is " << cnt << '\n';}

输出:

 

Final counter value is 10000

 

那么在什么情景下使用memory_order_relaxed呢?

 

由于Relaxed ordering 仅仅保证load()store()是原子操作,除此之外,不提供任何跨线程的同步。

 

如果某个操作只要求是原子操作,除此之外,不需要其它同步的保障,就可以使用 Relaxed ordering。程序计数器是一种典型的应用场景:

 

 
#include <cassert>#include <vector>#include <iostream>#include <thread>#include <atomic>std::atomic<int> cnt = {0};void f(){  for (int n = 0; n < 1000; ++n) {    cnt.fetch_add(1, std::memory_order_relaxed);  }}int main(){  std::vector<std::thread> v;  for (int n = 0; n < 10; ++n) {      v.emplace_back(f);    }  for (auto& t : v) {    t.join();  }  assert(cnt == 10000);    // never failed  return 0;}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值