很多时候写多线程应用程序,习惯弄一个原子变量来做线程间的同步。
比如代码
std::atomic<bool> ready = false;
p.Init();
ready = true;
if (ready) {
p.bar();
}
线程2在等待ready被设置,
代码可能会被优化成ready在Init之前被设置,这样就会出现问题了。
那么怎样避免代码被优化呢?
为了解决这个问题,cpu和编译器提供了memory fence,让用户可以声明访存指令的可见性关系,c++11总结为以下memory order:
memory order | 作用 |
---|---|
memory_order_relaxed | 无fencing作用,cpu和编译器可以重排指令 |
memory_order_consume | 后面依赖此原子变量的访存指令勿重排至此条指令之前 |
memory_order_acquire | 后面访存指令勿重排至此条指令之前 |
memory_order_release | 前面的访存指令勿排到此条指令之后。当此条指令的结果被同步到其他核的cache中时,保证前面的指令也已经被同步。 |
memory_order_acq_rel | acquare + release |
memory_order_seq_cst | acq_rel + 所有使用seq_cst的指令有严格的全序关系 |
代码改成
// Thread1 // ready was initialized to false
p.init();
ready.store(true, std::memory_order_release);
// Thread2
if (ready.load(std::memory_order_acquire))
{ p.bar(); }
补充下:c++11的原子操作是加了内存屏障的,所以放心大胆用。