C++11 Memory Model

Herb Sutter在一次对C++11内存模型的演讲中提到C++11的内存模型,让C++有了标准独立于编译器和平台线程库和标准的多线程内存控制方式。

咋一看这个句话很奇怪,难道C++98/03及以前的内存模型不支持多线程吗, 用C++03的标准不照样写多线程程序。 其实我忽略了一个事实在B家同学们都是gcc & linux,潜意识posix标准的pthread就是C++的线程库存,其实还有cl & windows:)

C++标准是基于一个抽象的机器制定的(大部分语言标准应该也是这样),它没有具体的CPU或编译器。 C++98/03标准没有对每次读写(loads and stores)以及执行顺序作出规定,所以无法写出完全可移植的多线程代码(能同时支持freebsd,linux和windows就很不错了)。C++11标准则在设计上引入了支持多线程的内存模型,它规定了在多线程环境中内存的读写的操作以及可能的执行顺序1

在cppreference.com上可以看到load和store的原型如下,

T load( std::memory_order order = std::memory_order_seq_cst ) const noexcept;
void store( T desired, std::memory_order order = std::memory_order_seq_cst ) noexcept;

对于memory order多线程环境中(CPU也是乱序执行的)的几种规定如下下表,

Value Explanation
memory_order_relaxed 对其它读写操作没有同步,只保证本操作是原子的
memory_order_consume load操作,当前线程依赖该原子变量的访存操作不能reorder到该指令之前,对其他线程store操作(release)可见
memory_order_acquire load操作,当前线程所有访存操作不能reorder到该指令之前,对其他线程store操作(release)可见
memory_order_release store操作,当前线程所有访存操作不能reorder到该指令之后,对其他线程load操作(consume)可见
memory_order_acq_rel load/store操作,memory_order_acquire + memory_order_release
memory_order_seq_cst memory_order_acq_rel + 顺序一致性(sequential consisten)

关于memory_order_seq_cst与memory_order_acq_rel,下面这段代码很直观的体现了,

#include 
#include 
#include 
 
std::atomic x = {false};
std::atomic y = {false};
std::atomic z = {0};
 
void write_x()
{
    x.store(true, std::memory_order_seq_cst);
}
 
void write_y()
{
    y.store(true, std::memory_order_seq_cst);
}
 
void read_x_then_y()
{
    while (!x.load(std::memory_order_seq_cst))
        ;
    if (y.load(std::memory_order_seq_cst)) {
        ++z;
    }
}
 
void read_y_then_x()
{
    while (!y.load(std::memory_order_seq_cst))
        ;
    if (x.load(std::memory_order_seq_cst)) {
        ++z;
    }
}
 
int main()
{
    std::thread a(write_x);
    std::thread b(write_y);
    std::thread c(read_x_then_y);
    std::thread d(read_y_then_x);
    a.join(); b.join(); c.join(); d.join();
    assert(z.load() != 0);  // will never happen
}


知乎上有个G家的stephen w很认真的翻阅了经典计算机体系结构:量化研究方法给了不错的总结2,

"SC要求所有内存操作表现为(appear)逐个执行(任一次的执行结果都像是所有处理器的操作都以某种次序执行),每个处理器中的操作都以其程序指定的次序执行。SC有两点要求:在每个处理器内,维护每个处理器的程序次序;在所有处理器间,维护单一的表征所有操作的次序。对于写操作W1, W2, 不能出现从处理器 P1 看来,执行次序为 W1->W2; 从处理器 P2 看来,执行次序却为 W2->W1 这种情况。


参考链接

  1. https://stackoverflow.com/questions/6319146/c11-introduced-a-standardized-memory-model-what-does-it-mean-and-how-is-it-g
  2. https://www.zhihu.com/question/24301047/answer/83422523

你可能感兴趣的:(C++11 Memory Model)