> 文档中心 > C++11多线程 内存屏障(fence/atomic_thread_fence)

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

China香烟网