Intel Threading Building Blocks 编程指南:原子操作
来源:互联网 发布:传智播客php视频教程 编辑:程序博客网 时间:2024/06/03 18:40
原子操作概述
可以使用原子操作来避免使用互斥。当一个线程执行原子操作,在其他线程眼里,这个操作是瞬时完成的。原子操作的优点是,相比较锁操作是快速的,而且不用为死锁、锁护送等问题而烦恼。缺点是,它们只有有限的一组操作,常常无法和成为有效的复杂操作。尽管如此,也不应该放弃使用原子操作替换互斥的机会。aotmic<T> 类以C++风格实现了原子操作。
原子操作的一个典型应用是线程安全的引用计数。设x是类型为 int 的引用计数,当它变为0时程序需要做一些操作。在单线程代码中,你可以使用 int 来定义 x,然后 --x;if ( x==0 ) action() 。但在多线程环境中,这种方法可能会失效,因为两个线程可能以下表的方式交替操作(其中的t(x)代表机器的寄存器)。
<span style="font-size:14px;">if(--x==0) action()</span>
读取 x 的值
x =
给 x 赋值,并返回它
x.fetch_and_store(y)
执行x=y,并返回x的旧值
x.fetch_and_add(y)
执行x+=y,并返回x的旧值
x.compare_and_swap(y,z)
如果x==z,执行 x=y . 返回x的旧值
atomic<unsigned> counter;unsigned GetUniqueInteger(){return counter.fetch_and_add(1);}
atomic<int> globalx;int UpdateX(){ // Update x and return old value of x. do{// Read globalX oldx = globalx;// Compute new value newx = ...expression involving oldx....// Store new value if another thread has not changed globalX. } while (globalx.compare_and_swap(newx, oldx) != oldx);return oldx;}
- 一个线程从 globalx 中读取值 A
- 其他的线程将 globalx 从 A 修改为 B ,再到 A
- 步骤1 的线程执行 compare_and_swap, 读取 A ,但没有检测到期间变化到 B
atomic<T>没有构造函数
atomic<T>模板类特意没有声明构造函数,因为诸如上述的 GetUniqueInteger 之类的例子一般要求在所有的文件作用域构造函数被调用前就可以工作。如果该模板类声明了构造函数,在它被引用后,也许要初始化一个文件作用域的实例。在下述上下文中,任何没有生命构造函数的 C++类的原子类型atomic<T> 的对象 X 被自动初始化为 0 :
- X 被声明为文件作用域变量,或者类的静态数据成员
- X 是类的成员,并且显式地出现在该类的构造函数的初始化列表中
下面的代码是对这些问题的解释
<span style="font-size:14px;">atomic<int> x; // 由于处于文件作用域,初始化为0 class Foo{atomic<int> y;atomic<int> notzeroed;static atomic<int> z;public:Foo() :y() // y 初始化为0. {// notzeroed has unspecified value here. }};atomic<int> Foo::z; // 静态成员,初始化为0</span>
内存一致性
一些计算机架构,比如Intel IA-64(安腾)系列,拥有“弱内存一致性”,对不同地址的内存操作出于效率方面的原因被重新排序。这是个复杂的话题,建议感兴趣的读者查阅其他资料。如果只是为IA-32 和 Intel 64 架构平台编程,可以忽略此节。
atomic<T> 类准许你强制某些内存排序操作,在下表列出:
描述
默认为
获取(acquire)
读
释放 (release)
原子操作之前的操作不会挪动它
写
连续性一致
任何一边的操作都不会挪动原子操作。并且连续性一直的原子操作有着总的顺序。
fetch_and_store
fetch_and_add
compare_and_swap
最右边列出了特定约束的默认操作。使用这些默认值来避免不期望的意外。对于读和写,默认值是仅有的有效约束。然而,如果你很熟悉弱内存一致性,你也许会想改变其他操作默认的连续一致性为弱约束。要做到这点,使用接受模版参数的变量。参数可以是 acquire 或者 release (枚举类型 memory_semantics 的值)。
例,假设一份数据结构的不同部分由不同的线程生成,完成后,你想通知一个订阅线程。一种方法是初始化一个原子计数为生产者的数量,当每个生产者结束时,执行:
refcount.fetch_and_add<release>(-1);参数 release 确保在 refcount 做减法之前生产者写共享内存。类似,如果订阅者检查 refcount ,它必须使用 acquire(默认为读)栅格,这样订阅者直到看见 refcount 变为 0 才会进行数据结构的读操作。
- Intel Threading Building Blocks 编程指南:原子操作
- Intel Threading Building Blocks 编程指南:原子操作
- 《Intel Threading Building Blocks编程指南》
- 准备学习《Intel Threading Building Blocks编程指南》
- Intel Threading Building Blocks 编程指南:任务调度
- Intel Threading Building Blocks 编程指南 : 内存分配
- Intel Threading Building Blocks 编程指南:互斥
- Intel Threading Building Blocks 编程指南:异常与终止
- [译]Intel Threading Building Blocks 编程指南:任务调度
- Intel Threading Building Blocks 编程指南 : 内存分配
- Intel Threading Building Blocks 编程指南:异常与终止
- Intel Threading Building Blocks 编程指南:互斥
- Intel Threading Building Blocks 基于任务编程
- Intel Threading Building Blocks
- Intel Threading Building Blocks
- Intel Threading Building Blocks 编程指南:简单循环的并行化
- Intel Threading Building Blocks 编程指南:简单循环的并行化
- Intel Threading Building Blocks技术文章
- 字节对齐
- java多线程实例
- Android的logcat命令详解
- Spark MLlib NaiveBayes 贝叶斯分类器
- qt配置wince 开发环境
- Intel Threading Building Blocks 编程指南:原子操作
- 为Druid监控配置访问权限(配置访问监控信息的用户与密码)
- 黑马程序员学习日记--java编程基础
- 计算机视觉相关的部分测试数据集和源码站点
- IOS类继承关系图 && 原生控件
- Java Socket应答与HTTP服务器的瓜葛
- Oracle 使用GRANT 给存储过程赋权限方法
- 仿腾讯QQ拍照 背景图片滑动获取图片
- genymotion安装(unknown generic error)