C++11——多任务内存模型
内存模型
一般来说,内存模型可以分为静态内存模型和动态内存模型
-
静态内存模型
主要是类对象在内存中的布局,也就是类成员在内存中是如何存放的。
-
动态内存模型
从行为方面来看,多个线程对同一对象同事读写时所作的约束。涉及了内存、Cache、CPU各个层次的交互,尤其是在多核系统中为了保证多线程下执行的正确性,需要对读写事件加以严格限制。
C++ 11的内存模型
std::memory_order
就是C++ 11的内存模型。
-
行为:synchronized-with
这是
std::atomic
生效的前提之一。 假设X是一个atomic变量,如果线程A写了变量X, 线程B读了变量X,那么我们就说线程A、B间存在synchronized-with关系。 C++11默认的原子操作(memory_order_seq_cst)就是synchronized-with的,保证了对X的读和写是互斥的,不会同时发生。 -
结果:happens-before
指明了后发生的动作会看到先发生的动作的结果。 当线程B读取X时,读到的一定是写入后的X值,而不会是其它情况。happends-before具有传递性。如果A happens-before B,B happens-before C,那么A happends-before C。
C++11为std::atomic提供的memory order:
enum class memory_order
{
memory_order_relaxed,
memory_order_consume, // load-consume
memory_order_acquire, // load-acquire
memory_order_release, // store-release
memory_order_acq_rel, // store-release load-acquire
memory_order_seq_cst // store-release load-acquire
};
虽然枚举定义了6个,但它们表示的是4种内存模型:
序号 | 内存模型 | memory_order值 |
---|---|---|
1 | 宽松 | memory_order_relaxed |
2 | 释放-获取 | memory_order_acquire memory_order_release memory_order_acq_rel |
3 | 释放-消费 | memory_order_consume |
4 | 顺序一致 | memory_order_seq_cst |
顺序一致性次序
顺序一致性次序是std::atomic
的默认内存序,它意味着将程序看做是一个简单的序列。如果对于一个原子变量的所有操作都是顺序一致的,那么多线程程序的行为就像是这些操作都以一种特定顺序被单线程程序执行。从同步的角度来看,一个顺序一致的store操作会与load操作同步。 顺序模型还保证了在load之后执行的顺序一致原子操作都得表现在store之后完成。
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);
}
最后
当程序对性能没有特殊要求时,首选std::mutex,其实使用memory_order_seq_cst。