发布于: 2023-10-9最后更新: 2023-10-9字数 00 分钟

内存模型

C++11的内存模型是一种规定了多线程并发操作如何交互的规则。这个模型主要由两部分组成:原子操作(atomic operations)和内存顺序(memory orders)。
原子操作
原子操作是一种特殊的操作,它们在执行期间不能被中断。换句话说,原子操作在执行过程中不会被其他线程的操作所影响。C++11为各种基础类型(如intfloatdouble,指针等)提供了原子版本,它们都在std::atomic命名空间下。
例如:
std::atomic提供了一系列的操作,比如load(), store(), fetch_add(), fetch_sub()等,它们都是原子的。
内存顺序
内存顺序定义了原子操作的执行顺序。C++11提供了以下几种内存顺序:
  • std::memory_order_relaxed:不强制执行任何顺序,只保证了单个线程中的操作顺序。
  • std::memory_order_consume:保证了本线程中,所有依赖于当前原子操作的后续操作,都不会在当前操作之前执行。
  • std::memory_order_acquire:标记读。保证了本线程中,所有在当前原子读操作之后的读写操作,都不会在当前操作之前执行。
  • std::memory_order_release:标记写。保证了本线程中,所有在当前原子写操作之前的读写操作,都不会在当前操作之后执行。
  • std::memory_order_acq_rel:同时具有acquirerelease的效果。
  • std::memory_order_seq_cst:全序,即在所有线程中都按照一致的顺序看待原子操作。
这些内存顺序对于控制线程间的同步非常有用。例如,使用std::memory_order_acquirestd::memory_order_release可以确保数据在被一个线程写入后,再被另一个线程读取。
示例
以下是一个使用std::memory_order的示例:
在这个例子中,std::memory_order_acquirestd::memory_order_release一起使用,确保了数据的正确同步。
这个模型的一个主要优点是,它提供了更精细的控制,以及可能的性能优化。然而,它也增加了编程的复杂性,需要更深入的理解以避免错误。

CAS操作和内存屏障

CAS(Compare And Swap)原子操作是一种常用的无锁同步策略。CAS原子操作包含三个操作数 —— 内存位置V、预期原值A和新值B。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置的值更新为新值B,否则,处理器不做任何操作。整个比较和替换的过程是一个原子操作。
使用示例:cpp里compare_exchange_weak方法执行CAS操作:如果value的当前值等于expected,那么就将value的值更新为updated,否则,就不做任何操作。参考:C++ 原子操作CAS和lockless无锁队列_c++ cas_雪*夹雨夹*雪的博客-CSDN博客
乱序执行(Out-of-Order Execution)是现代CPU采用的一种提高指令执行效率的技术。在乱序执行中,CPU会预先执行一些指令,然后再按照程序的顺序提交执行结果。这可能会导致多线程程序中的数据竞争问题。为了避免这个问题,我们可以使用内存屏障(Memory Barrier)或者原子操作来确保指令的执行顺序。内存屏障通常用于同步原语(如互斥锁)的实现中,以确保正确的内存可见性。例如,在C++11中,std::atomic的成员函数std::atomic::storestd::atomic::load就包含了内存屏障。
以下是一个使用了内存屏障的简单例子:
在这个例子中,std::memory_order_release内存屏障确保了data的赋值操作在ready的赋值操作之前对其他线程可见。std::memory_order_acquire内存屏障确保了在看到ready变为true之后,可以看到data的正确值。