C++11的6种内存序

简介

编译器的优化会产生乱序指令,cpu也会因为指令流水线优化产生乱序指令。当然这些乱序指令都是为了同一个目的,优化执行效率 。

这里解释下两个词的含义:

  • happens-before:按照程序的代码序执行
  • synchronized-with:不同线程间,对于同一个原子操作,需要同步关系,store操作一定要先于 load,也就是说 对于一个原子变量x,先写x,然后读x是一个同步的操作,读x并不会读取之前的值,而是写x后的值。

C++11有6种内存序

  • std::memory_order_relaxed(__ATOMIC_RELAXED)
  • std::memory_order_acquire (__ATOMIC_ACQUIRE)
  • std::memory_order_release (__ATOMIC_RELEASE)
  • std::memory_order_acq_rel (__ATOMIC_ACQ_REL)
  • std::memory_order_consume(__ATOMIC_CONSUME)
  • std::memory_order_seq_cst (__ATOMIC_SEQ_CST)

详解

memory_order_relaxed

不对执行顺序做保证,没有happens-before的约束,编译器和处理器可以对memory access做任何的reorder,这种模式下能做的唯一保证,就是一旦线程读到了变量var的最新值,那么这个线程将再也见不到var修改之前的值了。

同时当relaxed存一个数据的时候,另外的线程将需要一个时间才能relaxed读到该值,在非缓存一致性的构架上需要刷新缓存。在开发的时候,如果你的上下文没有共享的变量需要在线程间同步,选用Relaxed就可以了。

举个例子说明一下:

对于最初为零的 x 和 y

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

允许产生结果 r1 == 42 && r2 == 42,执行的顺序可能为D A B C。

memory_order_acquire 和memory_order_release

memory_order_acquire保证本线程中,所有后续的读操作必须在本条原子操作完成后执行。memory_order_release保证本线程中,所有之前的写操作完成后才能执行本条原子操作。

通常的做法是:将资源通过store+memory_order_release的方式”Release”给别的线程;别的线程则通过load+memory_order_acquire判断或者等待某个资源,一旦满足某个条件后就可以安全的“Acquire”消费这些资源了。即释放获得顺序

#include <thread>
#include <atomic>
#include <cassert>
#include <string>
 
std::atomic<std::string*> ptr;
int data;
 
void producer()
{
    std::string* p  = new std::string("Hello");
    data = 42;
    ptr.store(p, std::memory_order_release);
}
 
void consumer()
{
    std::string* p2;
    while (!(p2 = ptr.load(std::memory_order_acquire)))
        ;
    assert(*p2 == "Hello"); // 绝无问题
    assert(data == 42); // 绝无问题
}
 
int main()
{
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join(); t2.join();
}

memory_order_acq_rel则是同时包含memory_order_acquire 和 memory_order_release

释放消费顺序(release/consume)的规范正在修订中,而且暂时不鼓励使用 memory_order_consume

memory_order_seq_cst

该模型是最强的同步模式,同时也是默认的模型,具有强烈的happens-before语义,
来保证指令的顺序一致执行,相当于不打开编译器优化指令,按照正常的指令序执行。多线程各原子操作也会Synchronized-with,譬如atomic::load()需要等待atomic::store()写下元素才能读取。更进一步地,读操作需要在“一个写操作对所有处理器可见”的时候才能读,适用于基于缓存的体系结构。

#include <thread>
#include <atomic>
#include <cassert>
 
std::atomic<bool> x = {false};
std::atomic<bool> y = {false};
std::atomic<int> 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);  // 决不发生
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值