memory order

源码变成可执行程序,一般由预编译,编译,汇编,链接。源码重排序一般分为编译期重排序和运行期重排序。

编译期重排序:编译器在不改变单线程程序的语义的前提下,可以重新安排语句的执行顺序。在不改变程序的语义的前提下,尽可能减少寄存器的读取,存储次数,充分复用寄存器的存储值。

CPU乱序执行

名称 语义               
memory_order_relaxed Relaxed语义
memory_order_consume Release-Acquire语义
memory_order_acquire Release-Acquire语义
memory_order_release  Release-Acquire语义
memory_order_acq_rel  Release-Acquire语义
memory_order_seq_cst  Sequential consistency语义

synchronizes-with、happens-before关系

synchronizes-with: 如果线程A存储一个值,而线程B读取该值,那么线程A中的存储和线程B的载入之间存在一种synchronizes-with关系

happens-before: 如果一个操作排在另外一个操作之前,那么该操作就该发生于另一个操作之前

Relaxed语义

最宽松的内存操作约定,不会保证修改会不会及时被其他的线程看到,也不对乱序做任何要求

relaxed的原子类型操作不参与synchronizes-with关系。

不同变量的relaxed可以被自由的重排,前提它们服从所有约束下的happens-before关系

#include <atomic>
#include <thread>
#include <assert.h> atomic<bool> x, y;
atomic<int> z; void write_x_then_y()
{
x.store(true, memory_order_relaxed);
y.store(true, memory_order_relaxed);
} void read_y_then_x()
{
while (!y.load(memory_order_relaxed));
if (x.load(memory_order_relaxed))
++z;
}int main()
{
x = false;
y = false;
z = ;
thread a(write_x_then_y);
thread b(read_y_then_x);
a.join();
b.join();
assert(z.load() != ); // z可能等于0
}

Release-Acquire语义Sequential consistency语义

  release和acquire总是一起使用

  release用于写操作,acquire用于读操作

  release之前的写操作不允许乱序到release之后, acquire之前的读操作不允许乱序到acquire之前

  acquire的修改会及时被release看到

Sequential consistency语义

  sequential consistency 相当于 release + acquire 之外,还加上了一个对该操作加上全局顺序的要求

#include <atomic>
#include <thread>
#include <assert.h> atomic<bool> x, y;
atomic<int> z; void write_x()
{
x.store(true, memory_order_seq_cst);
} void write_y()
{
y.store(true, memory_order_seq_cst);
} void read_x_then_y()
{
while (!x.load(memory_order_seq_cst));
if (y.load(memory_order_seq_cst))
++z;
} void read_y_then_x()
{
while(!y.load(memory_order_seq_cst));
if (x.load(memory_order_seq_cst))
++z;
} int main()
{
x = false;
y = false;
z = ;
thread a(write_x);
thread b(write_y);
thread c(read_x_then_y);
thread d(read_y_then_x);
a.join();
b.join();
c.join();
d.join();
assert(z.load() != ); // z不可能等于0
}

store,load

atomic<bool> x, y;  
atomic<int> z; void write()
{
x.store(true, memory_order_relaxed);
y.store(true, memory_order_release); // x的值比y先填充值
} void read()
{
while(!y.load(memory_order_acquire)); // y在等write值得填充
if (x.load(memory_order_relaxed)) {
z++;
}
} int main()
{
x = false;
y = false;
z = ; thread t1(write);
thread t2(read);
t1.join();
t2.join(); cout << "z: " << z << endl;
}

exchage

compare_exchange_weak

bool compare_exchange_weak(T& expected, T val, memory_order sync = memory_order_seq_cst) 

NOTE:

1. atomic变量的值与expected进行比较, 如果结果true, 用val更新atomic的值(like store); 如果结果为false, 用atomic的值更新expected的值.

2. 这个函数能获得这个atomic变量的值,并且如果比较的结果是true的话就修改这个值. 整个操作是原子操作,在读取或者修改这个值得瞬间,其他的线程不会修改这个值.

3. memory_order 是否生效也是根据比较的结果,如果结果为true,那么生效,否则不生效

4. compare_exchage_weak允许伪失败(fail spuriously),尽管expected值确实和atomic变量的值相等,仍然会返回false; 这需要使用while操作

#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
using namespace std; struct Node {
int value;
Node *next;
}; atomic<Node*> list_head(nullptr);
void Append(int val)
{
Node *p_old_node = list_head;
Node *p_new_node = new Node {val, p_old_node}; while (!list_head.compare_exchange_weak(p_old_node, p_new_node)) {
p_new_node->next = p_old_node;
}
} int main()
{
vector<thread> threads;
for (int i = ; i < ; i++) {
threads.push_back(thread(Append, i));
} for (auto &i : threads) {
i.join();
} for (Node *it = list_head; it != nullptr; it = it->next) {
cout << it->value << " ";
}
cout << endl;
}

compare_exchange_strong

bool compare_exchange_strong (T& expected, T val, memory_order sync = memory_order_seq_cst)

1. compare_exchange_strong的用法和compare_exchange_weak基本用法都一样

2. compare_exchange_strong不允许伪失败(fail spuriously)

3. compare_exchange_weak的循环结构在某些机器上可能有更好的性能

atomic<int> ai;
int tst_val = ;
int new_val = ;
bool exchanged = false; void valsout()
{
cout << "ai: " << ai << " tst_val: " << tst_val << " new_val: " << new_val << " exchanged: " << boolalpha << exchanged << endl;
} int main()
{
ai = ;
valsout(); //ai = 3, tst_val = 4, new_val = 5, exchanged = false; exchanged = ai.compare_exchange_strong(tst_val, new_val);
valsout(); //ai = 3, tst_val = 3, new_val = 5, exchanged = false; exchanged = ai.compare_exchange_strong(tst_val, new_val);
valsout(); //ai = 3, tst_val = 3, new_val = 5, exchanged = false;
}

参考资料:

[1] http://www.cplusplus.com/reference/atomic/atomic/

[2] <<c++并发编程>>

[3] http://www.cnblogs.com/haippy/p/3252056.html

[4] http://www.cnblogs.com/muhe221/articles/5049474.html

atomic用法的更多相关文章

  1. boost并发编程boost::atomic

    三个用于并发编程的组件: atomic,thread,asio(用于同步和异步io操作)   atomic atomic,封装了不同计算机硬件的底层操作原语,提供了跨平台的原子操作功能,解决并发竞争读 ...

  2. C ++ _多线程笔记

    #include<iostream>#include <thread>//创建线程需要添加的头文件 using namespace std;/*thread join(阻塞:主 ...

  3. Java:多线程,java.util.concurrent.atomic包之AtomicInteger/AtomicLong用法

    1. 背景 java.util.concurrent.atomic这个包是非常实用,解决了我们以前自己写一个同步方法来实现类似于自增长字段的问题. 在Java语言中,增量操作符(++)不是原子的,也就 ...

  4. atomic 原子自增工程用法案例

    案例 1 : 简单用法 atomic_int id; atomic_fetch_add(&id, 1) atomic_uint id; atomic_fetch_add(&id, 1) ...

  5. 原子操作--sync/atomic的用法

    golang 通过sync/atomic库来支持cpu和操作系统级别的原子操作.但是对要操作类型有如下要求 int32, int64,uint32, uint64,uintptr,unsafe包中的P ...

  6. 多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类)

    前言:刚学习了一段机器学习,最近需要重构一个java项目,又赶过来看java.大多是线程代码,没办法,那时候总觉得多线程是个很难的部分很少用到,所以一直没下决定去啃,那些年留下的坑,总是得自己跳进去填 ...

  7. java并发:线程同步机制之Volatile关键字&原子操作Atomic

    volatile关键字 volatile是一个特殊的修饰符,只有成员变量才能使用它,与Synchronized及ReentrantLock等提供的互斥相比,Synchronized保证了Synchro ...

  8. C++11 并发指南六(atomic 类型详解三 std::atomic (续))

    C++11 并发指南六( <atomic> 类型详解二 std::atomic ) 介绍了基本的原子类型 std::atomic 的用法,本节我会给大家介绍C++11 标准库中的 std: ...

  9. nonatomic, retain,weak,strong用法详解

    strong weak strong与weak是由ARC新引入的对象变量属性 ARC引入了新的对象的新生命周期限定,即零弱引用.如果零弱引用指向的对象被deallocated的话,零弱引用的对象会被自 ...

随机推荐

  1. 配置maven环境变量并安装jar包到本地仓库

    1.下载maven安装包,解压,解压目录如下: 2.配置M2_HOME变量为上一步的路径: 3.配置PATH变量,添加%M2_HOME%\bin;  查看是否配置成功 mvn -v : 4.安装jar ...

  2. python pickle/cPickle模块

    序列化(picking): 把变量从内存中变成可存储或传输的过程称为序列化,序列化之后,就可以把序列化的对象写入磁盘,或者传输给其他设备; 反序列化(unpickling):相应的,把变量的内容从序列 ...

  3. 「NOI1999」「LuoguP1731」生日蛋糕(爆搜剪枝

    题目背景 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层 生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1<=i<=M)层蛋糕是半径为Ri, 高度为Hi的圆柱 ...

  4. 「NOI2015」「Codevs4621」软件包管理器(树链剖分

    4621 [NOI2015]软件包管理器 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 钻石 Diamond   题目描述 Description Linux用户和OSX用户一定对 ...

  5. ubuntu 16.04 安装 Matlab R2016b后启动出现的问题

    (1)报以下错误: License checkout failed.License Manager Error -95MATLAB is unable to connect to the licens ...

  6. 注册页面Page的内置属性以及函数 路由 模块化

    Page.prototype.route  route字段可以获取到当前页面的路径 Page.prototype.setData() setData函数用于将数据从逻辑层发送到视图层,同时改变对应的t ...

  7. js的一些刷新功能

    <a href="javascript:location.replace(location.href);" title="刷新"> </a&g ...

  8. Windows下安装zip包解压版mysql

    Windows下安装zip包解压版mysql 虽然官方提供了非常好的安装文件,但是有的时候不想每次再重装系统之后都要安装一遍MySQL,需要使用zip包版本的MySQL.在安装时需如下三步: 1. 新 ...

  9. web开发并部署到Tomcat上

    1. eclipse配置tomcat https://jingyan.baidu.com/article/e4d08ffdabb0710fd2f60de9.html https://blog.csdn ...

  10. 利用JavaScript选择GridView行

    本篇技巧和诀窍记录的是:利用JavaScript选择GridView行. 当我们想在GridView中添加删除.选择功能时,我们通常的做法是利用模板功能在每行添加一个按钮控件或者超链接按钮控件,单击按 ...