很多时候写多线程应用程序,习惯弄一个原子变量来做线程间的同步。
比如代码
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的原子操作是加了内存屏障的,所以放心大胆用。
本文探讨了在多线程编程中如何使用C++11的原子操作和内存屏障来避免代码优化导致的问题。通过std::atomic变量和memory_order属性,确保线程间的同步正确性,防止编译器和CPU指令重排序。示例展示了如何使用memory_order_release和memory_order_acquire来保证数据一致性。
1219

被折叠的 条评论
为什么被折叠?



