C++11多线程 内存屏障(fence/atomic_thread_fence)
0 引言
在前述文章中,我有针对C++11种的六种内存序(memory order)进行相应的讲解,其文章如下
C++11多线程 内存序(std::memory_order_seq_cst )
C++11多线程 内存序(std::memory_order_consume)
C++11多线程 内存序(std::memory_order_acquire/release)
C++11多线程 内存序(std::memory_order_relaxed)
如果不补充C++11种关于fence的讲解,那么内存序这部分是不完善的,故此篇文章便是为此而写。
本文主要讲解C++11种fence的主要概念和使用,关于其和原子操作的内存序相关的比较,会放到下一篇文章中。
1 fence基本概念
如果C++原子操作库没有规定一组相应的fences,那么这个原子库是不完备的。fence操作在不修改任何数据的情况下强制执行内存排序约束,并且通常与使用 memory_order_relaxed 排序约束的原子操作相结合。
fence是全局操作,会影响执行fence的线程中其他原子操作的顺序。 fence通常也被称为内存屏障,它们之所以得名,是因为它们在代码中放置了一条特定操作无法跨越的屏障。
C++中支持两种类型的fence:std::atomic_thread_fence 和 a std::atomic_signal_fence.
本文仅仅讲解std::atomic_thread_fence!
std::atomic_thread_fence 防止特定操作越过fence。 std::atomic_thread_fence 不需要原子变量。
上述中,特定的操作可归为如下四类
-
LoadLoad: 一个load操作后跟一个load
-
LoadStore: 一个load操作后跟一个store
-
StoreLoad: 一个store操作后跟一个load操作
-
StoreStore: 一个store操作后跟一个load操作
2 fence分类
C++11中提供了三种fences,分别为full fence, acquire fence 和 release fence.
Full Fence:
std::atomic_thread_fence() 默认为full fence,如果该fence放置在任意两个操作之间,那么可以防止这两个操作被重排序。注意:针对StoreLoad操作,其不能生效
其行为如下所示
Acquire Fence:
std::atomic_thread_fence(std::memory_order_acquire)防止在其前面的read操作被fence后面的读写操作重排序
其行为如下所示
Release Fence:
std::atomic_thread_fence(std::memory_order_release) 防止fence之后的写操作被fence之前的读或写操作重新排序
其行为如下所示
总体而言,三种fences针对四种操作组合的作用如下所示
3 示例代码
本章示例代码来自《C++ Concurrency in action》相应章节
#include #include #include std::atomic x,y;std::atomic z;void write_x_then_y() { x.store(true,std::memory_order_relaxed); // 1 std::atomic_thread_fence(std::memory_order_release); // 2 y.store(true,std::memory_order_relaxed); // 3}void read_y_then_x() { while(!y.load(std::memory_order_relaxed)); // 4 std::atomic_thread_fence(std::memory_order_acquire); // 5 if(x.load(std::memory_order_relaxed)) { // 6 ++z; }}int main() { x=false; y=false; z=0; std::thread a(write_x_then_y); std::thread b(read_y_then_x); a.join(); b.join(); assert(z.load()!=0); // 7 return 0;}
上述代码中 注释7 处的assert永远不会触发。
具体原因可参考如下所示
也即,std::atomic_thread_fence(std::memory_order_release)的release操作同步于其acquire操作,而x的store操作happend-before std::atomic_thread_fence(std::memory_order_release),因此其happen-before x的load操作。
4 总结
本文初步介绍了C++11中的fence概念及其基本使用,通过本篇文章可以加深对相应fence的理解。
参考:
Acquire and Release Fences
C++ Concurrency in Action, Second Edition