内存屏障和内存序本质上都是相同的,通过一定的指令来防止单线程视角下的指令重排。
C++内存序的本质即内存屏障。

内存屏障

只考虑三种简单的
smp_rmb():只排序读操作的” 读内存屏障 “ ,在读屏障之后(前)的所有的读指令,不可以越过读屏障,重新排序到读屏障之前(后)
smp_wmb():只排序写操作的” 写内存屏障 “,在写屏障之后(前)的所有的写指令,不可以越过写屏障,重新排序到写屏障之前(后)
smp_mb():只排序读和写操作的内存屏障。基本上就意味着读写指令的排序不可越过屏障。
整体上看屏障只有一种功能,禁止指令越过屏障。
由此,通过全局变量及其原子操作就相当于在多线程之间以屏障为中心建立了一个多线程之间的同步点。
int i = 0;
std::atomic<bool> flag(false);
//线程1
void writer(){
    i = 1024;
    smp_wmb();//屏障同步点,flag无法越过屏障执行
    //那么就保证了不会出现flag=true先执行,i还没赋值,i=0被reader读到的情况
    flag = true;
}
//线程2
void reader(){
   while(!flag){}
   smp_rmb();//同样的防止读取i先于flag读取
   //如果这里不用屏障进行同步,仍然有可能乱序,导致结果与预期不一致。
   cout << i << endl;
}

C++内存序

C++共有六种内存序,本质上可以看成是内存屏障的一种封装。
  • memory_order_relaxed: 最宽松的内存序,不提供任何同步保证。它只保证原子操作本身是原子的,但不保证操作之间的顺序
  • memory_order_consume: 消费者内存序,用于同步依赖关系。它保证了依赖于原子操作结果的后续操作将按照正确的顺序执行,某种程度上是memory_order_acquire
  • memory_order_acquire: 获取内存序,用于同步对共享数据的访问。它保证了在获取操作之后对共享数据的所有读取操作都将看到最新的数据。某种程度上等价于读屏障(smp_rmb)
  • memory_order_release: 释放内存序,用于同步对共享数据的访问。它保证了在释放操作之前对共享数据的所有写入操作都已完成,并且对其他线程可见。某种程度上等价于写屏障(smp_wmb)
  • memory_order_acq_rel: 获取-释放内存序,结合了获取和释放两种内存序的特点。它既保证了获取操作之后对共享数据的所有读取操作都将看到最新的数据,又保证了在释放操作之前对共享数据的所有写入操作都已完成,并且对其他线程可见。某种程度上等价于smp_mb()
  • memory_order_seq_cst: 顺序一致性内存序,提供了最严格的同步保证。它保证了所有线程都将看到相同的操作顺序,并且所有原子操作都将按照程序顺序执行。

memory_order_consume与memory_order_acquire

std::atomic<int> ato_i;
int a;
int b;

void reader_consume(){
    a = ato_i.load(std::memory_order_consume);
    b = 123;//因为b与ato_i没有联系,如果用memory_order_consume,则b可能重排到load之前
    /*
    b = 123;
    a = ato_i.load(std::memory_order_consume);
    */
    //memory_order_acquire则b不会越过这个屏障
}

memory_order_acq_rel与memory_order_seq_cst

//memory_order_acq_rel可以看成如下代码
memory_order_acquire //等价于一个读屏障smp_rmb()
指令1
指令2
指令3 
/*
但中间的指令可以在两者之间任意重排而不越过屏障
指令3
指令2
指令1 
*/
memory_order_acq_rel //smp_wmb()

//memory_order_seq_cst可以看成如下代码
memory_order_acquire //等价于一个读屏障smp_rmb()
//memory_order_seq_cst则使得中间的指令重排也被禁止
指令1
指令2
指令3 
memory_order_acq_rel //smp_wmb()

(99+ 封私信 / 80 条消息) 请问memory_order_acq_rel和memory_order_seq_cst究竟有什么区别? – 知乎

 

最后修改日期: 2024年10月26日

作者