概念:

摘录自:http://preshing.com/20120913/acquire-and-release-semantics/

Acquire semantics is a property which can only apply to operations which read from shared memory, whether they are read-modify-write operations or plain loads. The operation is then considered a read-acquire. Acquire semantics prevent memory reordering of the read-acquire with any read or write operation which follows it in program order.

Release semantics is a property which can only apply to operations which write to shared memory, whether they are read-modify-write operations or plain stores. The operation is then considered a write-release. Release semantics prevent memory reordering of the write-release with any read or write operation which precedes it in program order.

Acquire and Release Fences

First things first: Acquire and release fences are considered low-level lock-free operations. If you stick with higher-level, sequentially consistent atomic types, such as volatile variables in Java 5+, or default atomics in C++11, you don’t need acquire and release fences. The tradeoff is that sequentially consistent types are slightly less scalable or performant for some algorithms.

On the other hand, if you’ve developed for multicore devices in the days before C++11, you might feel an affinity for acquire and release fences. Perhaps, like me, you remember struggling with the placement of some lwsync intrinsics while synchronizing threads on Xbox 360. What’s cool is that once you understand acquire and release fences, you actually see what we were trying to accomplish using those platform-specific fences all along.

Acquire and release fences, as you might imagine, are standalone memory fences, which means that they aren’t coupled with any particular memory operation. So, how do they work?

An acquire fence prevents the memory reordering of any read which precedes it in program order with any read or write which follows it in program order.

release fence prevents the memory reordering of any read or write which precedes it in program order with any write which follows it in program order.

In other words, in terms of the barrier types explained here, an acquire fence serves as both a #LoadLoad + #LoadStore barrier, while a release fence functions as both a #LoadStore + #StoreStore barrier. That’s all they purport to do.

LoadLoad确保前后两个Load操作不乱序,StoreStore确保前后两个Store操作不乱序。 PowerPC上通过 lwsync 轻量级sync
StoreLoad 是最昂贵的。类似于磁盘的sync操作,确保将高速缓存中数据完全写入主内存;并确保其它CPU cache更新。PowerPC上通过 sync

编程接口:

C++11用法:

#include <atomic>
std::atomic_thread_fence(std::memory_order_acquire);
std::atomic_thread_fence(std::memory_order_release);

C11 用法:

#include <stdatomic.h>
atomic_thread_fence(memory_order_acquire);
atomic_thread_fence(memory_order_release);

以 C11 为例详细解释头文件 <stdatomic.h> 中定义的 memory_order 枚举的每个值的意思

enum memory_order {
memory_order_relaxed, /* 仅仅确保读写操作的原子性。无内存序,所以仅适用 atomic 变量 */
memory_order_consume, /* 数据依赖序,DEC Alpha only */
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
};

关于 C11  compare and exchange 各自版本的操作区别:

weak 和 strong

循环中用 weak 有更好的性能。 非循环操作必须用 strong 版本。因为 weak 有时候会在 所比较的值相等时候 也失败返回。

implicit 和 explicit

implicit 版本会默认 使用强内存模型 memory_order_seq_cst 。

explicit 版本会有2个额外参数 succ 和 fail,succ 指定 compare 比较成功后的内存 barrier;fail 指定 compare 失败后的内存 barrier 。

C 11 对各自的英文解释,比较绕口:

Value Explanation  
memory_order_relaxed Relaxed   ordering: there are no constraints on reordering of memory accesses around   the atomic variable. 确保操作原子性
memory_order_consume Consume   operation: no reads in the current thread dependent on the value currently   loaded can be reordered before this load. This ensures that writes to   dependent variables in other threads that release the same atomic variable   are visible in the current thread. On most platforms, this affects compiler   optimization only. 简言之 Data dependency barriers,比 Acquire 更弱。一般CPU都会自动保证数据依赖序(Alpha 除外)
memory_order_acquire Acquire   operation: no reads in the current thread can be reordered before this load.   This ensures that all writes in other threads that release the same atomic   variable are visible in the current thread. 其它线程Release之前的所有内存可见
memory_order_release Release   operation: no writes in the current thread can be reordered after this store.   This ensures that all writes in the current thread are visible in other   threads that acquire the same atomic variable. 此Release操作之前的所有内存,其它线程Acquire后可见;
此Release操作之前的部分内存,其它线程Consume后可见;
memory_order_acq_rel Acquire-release operation: no reads in the current thread can be reordered   before this load as well as no writes in the current thread can be reordered   after this store. The operation is read-modify-write operation. It is ensured   that all writes in another threads that release the same atomic variable are   visible before the modification and the modification is visible in other   threads that acquire the same atomic variable. Acquire和Release操作的合体。自动对读做Aquire操作;对写做Release操作
memory_order_seq_cst Sequential ordering. The operation has the same semantics as acquire-release   operation, and additionally has sequentially-consistent operation ordering.

a full memory fence 
比Acquire-release更进一步:之前所有写,其它线程立即可见(其它线程简单的读就能读到,不需要acquire)

频繁使用可能会成为性能瓶颈

重点:解释下什么情况下需要 memory_order_consume (data dependency barrier)

1)
A=
<data dependency barrier>
B=*A   
 
2)
A=
<data dependency barrier>
C=B[A]

问题:已经有封装好的 atomic 变量了,那 atomic_thread_fence 还有用场吗?

有用场。如下面例子,开始只有 relaxed 保证原子性,仅仅当读到变量满足条件时,才用 acquire 确保 do_work() 发生在 读到 mailbox[i] 之后

样例来自 http://en.cppreference.com/w/cpp/atomic/atomic_thread_fence

const int num_mailboxes = ;
std::atomic<int> mailbox[num_mailboxes]; // The writer threads update non-atomic shared data and then update mailbox[i] as follows
std::atomic_store_explicit(&mailbox[i], std::memory_order_release); // Reader thread needs to check all mailbox[i], but only needs to sync with one
for (int i = ; i < num_mailboxes; ++i) {
if (std::atomic_load_explicit(&mailbox[i], std::memory_order_relaxed) == my_id) {
std::atomic_thread_fence(std::memory_order_acquire); // synchronize with just one writer
do_work(i); // guaranteed to observe everything done in the writer thread before
// the atomic_store_explicit()
}
}

C11 memory_order的更多相关文章

  1. c89、c99、c11区别

    c89 c99 注: GCC支持C99, 通过 --std=c99 命令行参数开启,如: 代码:gcc --std=c99 test.c ------------------------------- ...

  2. gcc/g++ 如何支持c11 / c++11标准编译

    如果用命令 g++ -g -Wall main.cpp  编译以下代码 : /* file : main.cpp */ #include <stdio.h> int main() { in ...

  3. 【转】gcc/g++ 如何支持c11 / c++11标准编译

     如果用命令 g++ -g -Wall main.cpp  编译以下代码 : 1 2 3 4 5 6 7 8 9 10 11 12 /*     file : main.cpp */ #include ...

  4. C89, C99, C11: All the specifics that I know

    before anything.. sizeof is an operand!  sizeof is an operand! sizeof is an operand! 重要なことは三回にしませんね! ...

  5. [C/C++语言标准] ISO C99/ ISO C11/ ISO C++11/ ISO C++14 Downloads

    语言法典,C/C++社区人手一份,技术讨(hu)论(peng)必备 ISO IEC C99 https://files.cnblogs.com/files/racaljk/ISO_C99.pdf IS ...

  6. 是我out了,c11标准出炉鸟

    gcc -std=c11 -Wall -O3 -g0 -s -o x.c x 或者 clang -std=c11 -Wall -O3 -g0 -s -o x.c x 来吧! 我是有多无聊啊 测试代码: ...

  7. [转载]哪个版本的gcc才支持c11

    转自:https://blog.csdn.net/haluoluo211/article/details/71141093 哪个版本的gcc才支持c11 2017年05月03日 19:25:43 Fi ...

  8. 通过atomic_flag简单自旋锁实现简单说明标准库中锁使用的memory_order

    在使用标准库中的加锁机制时,例如我们使用std::mutex,写了如下的代码(下面的代码使用condition_variable可能更合适) std::mutex g_mtx; int g_resNu ...

  9. STL-容器库101--array【C11】

    1. 原型 C11提供 template < class T, size_t N > class array; T: 元素类型,以 array::value_type 作为别名使用:N: ...

随机推荐

  1. UltraEdit的语法高亮显示配置

    今天吴同学看到我电脑中有UltraEdit好奇地问我会不会用,我那个汗啊,不会用我装它干什么啊?其实当时装UltraEdit主要是用来写Java的,没有想到,工作一忙顾及不上学习Java的事情了.于是 ...

  2. C# System.Object基类

    System.Object 基类 System.Object在.Net中是所有类型的基类,任何类型都直接或间接地继承自System.Object.没有指定基类的类型都默认继承于System.Objec ...

  3. Linux打开windows 的txt文件,乱码的问题

    实际是两种不同操作系统对中文编码的不一样.转换下就行了 references: http://www.cnblogs.com/no7dw/archive/2013/05/21/3090594.html

  4. jquery中使用offset()获得的div的left=0,top=0

    写东西的时候要获取div的left和top,但怎么也取不到值都为0,但在chrome的console下是可以取到值的, 瞬间就纳闷了,于是乎就在网上找各种方法,大家一般的问题可能都是要获取的div被隐 ...

  5. android JNI调用 execlp函数

    execlp()函数           execlp函数简单的来说就是C语言中执行系统命令的函数          execlp()会从PATH 环境变量所指的目录中查找符合参数file 的文件名, ...

  6. Linux 文件

    Linux系统中:.a文件是静态链接库文件.所谓静态链接是指把要调用的函数或者过程链接到可执行文件中,成为可执行文件的一部分.当多个程序都调用相同函数时,内存中就会存在这个函数的多个拷贝,这样就浪费了 ...

  7. HibernateTemplate和HibernateDaoSupport

    Spring整合Hibernate后,为Hibernate的DAO提供了两个工具类:HibernateTemplate和HibernateDaoSupport HibernateTemplateHib ...

  8. “玲珑杯”郑州轻工业学院第八届ACM程序设计大赛暨河南高校邀请赛-正式赛(总结)

    这次轻院校赛,我们去了五个队,怀着打酱油的心态早早爬起来坐上校车出发了,由于昨晚室友打游戏,以及看视频大笑...没睡好,快1点才睡着,感觉特别困,车上没地方,睡不着,就在车上闭目养神,由于在新校区,不 ...

  9. JVM工作原理和特点(一些二逼的逼神面试官会问的问题)

    作为一种阅读的方式了解下jvm的工作原理 ps:(一些二逼的逼神面试官会问的问题) JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完毕,通过以下4步来完毕JVM环境. ...

  10. 树的直径 poj 2631

    树的直径:从随意一点出发,BFS找到最远的距离,然后在从该点出发BFS找到最远的距离 #include <iostream> #include <algorithm> #inc ...