原文: https://my.oschina.net/u/2516597/blog/805489
背景
C++11开始支持多线程,其中提供了原子类型atomic, 和atomic关系比较密切的是memory_order,所有的内存模型都是指atomic类型
enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
};a
具体
std::memory_order_seq_cst
首先是最严格的模型sequentially consistent, 下面的代码可以保证assert一定正确。
-Thread 1- -Thread 2-
y = 1 if (x.load() == 2)
x.store (2); assert (y == 1)
这里的store和load没有显示指定model的话,默认就是std::memory_order_seq_cst,该模式保证如果在线程2中拿到x在1中存储的值,那么线程2就可以在这看到线程1中所有发生在x.store(2)之前操作(主要是就是写操作),即使变量和x没有关系,是不是atmoic也无所谓,对于线程2来说,都是可见的。(有点疑惑,这和acquire和release有啥区别)
std::memory_order_relaxed
该模式仅仅保证了读写的完整性(不会读取到写一半的数据,要么是新值,要么是旧值),以及该原子的修改顺序一致性。
#include <atomic>
#include <thread>
#include <cassert>
std::atomic<bool> x,y;
std::atomic<int> z;
void write_x_then_y()
{
x.store(true,std::memory_order_relaxed); // 1
y.store(true,std::memory_order_relaxed); // 2
}
void read_y_then_x()
{
while(!y.load(std::memory_order_relaxed)); // 3
if(x.load(std::memory_order_relaxed)) // 4
++z;
}
int main()
{
x=false; //5
y=false; //6
z=0; //7
std::thread a(write_x_then_y); //A
std::thread b(read_y_then_x); //B
a.join();
b.join();
assert(z.load()!=0); // 8
return 0;
}
assert是仍然有可能触发的! 內存模型是std::memory_order_relaxed, 根据这个内存模型的说明,1,2处可能乱序,5,6和7也可能重拍乱序。所以在3和4处的read操做就可能即便y load到了true, 而x仍然load到false.
再举一个例子:下面的assert就不能出错。因为该原子在线程1中的顺序一定是正确的。
-Thread 1-
x.store (1, memory_order_relaxed)
x.store (2, memory_order_relaxed)
-Thread 2-
y = x.load (memory_order_relaxed)
z = x.load (memory_order_relaxed)
assert (y <= z)
std::memory_order_acquire/release
这个模式上面两个模式的混合, 下面的代码是x,y初始值都是0,在该模式下两个assert是可以通过的,但是如果不使用该模式的话,肯定只能通过,好好理解这个东西
-Thread 1-
y.store (20, memory_order_release);
-Thread 2-
x.store (10, memory_order_release);
-Thread 3-
assert (y.load (memory_order_acquire) == 20 && x.load (memory_order_acquire) == 0)
-Thread 4-
assert (y.load (memory_order_acquire) == 0 && x.load (memory_order_acquire) == 10)
参考
https://gcc.gnu.org/wiki/Atomic/GCCMM/AtomicSync