内存模型
C++11的内存模型是一种规定了多线程并发操作如何交互的规则。这个模型主要由两部分组成:原子操作(atomic operations)和内存顺序(memory orders)。
原子操作
原子操作是一种特殊的操作,它们在执行期间不能被中断。换句话说,原子操作在执行过程中不会被其他线程的操作所影响。C++11为各种基础类型(如
int
,float
,double
,指针等)提供了原子版本,它们都在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
:同时具有acquire
和release
的效果。
std::memory_order_seq_cst
:全序,即在所有线程中都按照一致的顺序看待原子操作。
这些内存顺序对于控制线程间的同步非常有用。例如,使用
std::memory_order_acquire
和std::memory_order_release
可以确保数据在被一个线程写入后,再被另一个线程读取。示例
以下是一个使用
std::memory_order
的示例:在这个例子中,
std::memory_order_acquire
和std::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::store
和std::atomic::load
就包含了内存屏障。以下是一个使用了内存屏障的简单例子:
在这个例子中,
std::memory_order_release
内存屏障确保了data
的赋值操作在ready
的赋值操作之前对其他线程可见。std::memory_order_acquire
内存屏障确保了在看到ready
变为true
之后,可以看到data
的正确值。- 作者:Olimi
- 链接:https://olimi.icu/article/ae86c94d-5e53-48cb-a305-7d04df22f211
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。