原子操作

原文链接:https://blog.csdn.net/just_kong/article/details/99289539

1、原子操作

原子操作(atomic operation)指的是由多步操作组成的一个操作。如果该操作不能原子的执行,则要么执行完所有步骤,要么一步也不执行,不可能只执行所有步骤的一个子集。

现代操作系统中,一般都提供了原子操作来实现一些同步操作,所谓原子操作,也就是一个独立不可分割的操作。在单核系统中,一般意义下原子操作中线程不会被切换,线程切换要么在原子操作前,要么在原子操作后。更广泛意义下原子操作是指一系列必须整体完成的步骤,如果任何一步操作没有完成,那么所有完成的步骤必须回滚,这样就可以保证要么所有的操作都没进行,要么都被完成。

例如在单核系统中,单个机器指令可以看成是原子操作(编译器优化、乱序执行等情况除外);在多核系统中,单个的机器指令就不是原子操作,因为多核系统里是多指令流并行运行的,一个核在执行一个指令时,其他核同时执行的指令可能操作在同一个内存区域,从而出现数据竞争的现象。多核系统中的原子操作通常使用内存删障(memory barrier)来实现,即一个CPU核在执行原子操作时,其他CPU核必须停止对内存的操作或者不对指定的内存操作,避免数据竞争问题。

2、windows原子操作api
Win32 API中常用的原子操作主要有三类,一种是加1减1操作,一种是比较交换操作,另外一种是赋值(写)操作。

(1)原子加1操作
LONG InterlockedIncrement( LONG volatile* Addend);

(2)  比较并交换操作
LONG InterlockedCompareExchange( LONG volatile*Destination, LONG Exchange, LONG Comperand );

这个操作是先将Comperand的值和Destination指向变量的值进行比较,如果相等就将Exchange变量的值赋给Destination指向的变量。返回值为未修改前的Destination位置的初始值。

(3)原子写操作
LONG InterlockedExchange( LONG volatile* Target, LONG Value);

InterlockedExchange的作用为将Value的值赋给Target指向的变量,返回Target指向变量未被赋值前的值。

3、GCC编译器提供的原子操作API
type __sync_fetch_and_add (type *ptr, type value);
type __sync_fetch_and_sub (type *ptr, type value);
type __sync_fetch_and_or (type *ptr, type value);
type __sync_fetch_and_and (type *ptr, type value);
type __sync_fetch_and_xor (type *ptr, type value);
type __sync_fetch_and_nand (type *ptr, type value);
type __sync_add_and_fetch (type *ptr, type value);
type __sync_sub_and_fetch (type *ptr, type value);
type __sync_or_and_fetch (type *ptr, type value);
type __sync_and_and_fetch (type *ptr, type value);
type __sync_xor_and_fetch (type *ptr, type value);
type __sync_nand_and_fetch (type *ptr, type value);

4、C++11提供的原子操作
C++11中在<atomic>中定义了atomic模板类,atomic的模板参数类型可以为int、long、bool等等,C++中称为trivially copyable type。atomic_int、atomic_long为atomic模板实例化后的宏定义。atomic具体的原子操作函数可以参考http://www.cplusplus.com/reference/atomic/atomic/?kw=atomic。

5、原子操作的效率
(1)不加锁也不使用原子变量的程序
程序代码:

输出:

(2)加锁的程序
程序代码:

输出:

3.使用C++11原子变量的程序
程序代码和输出:

 (4)结论
上面的第一个不加锁程序肯定是最不推荐使用的,因为它的执行结果都不正确。第二个程序是使用的常规锁来解决问题,结果正确,但是耗时较久。第三个程序使用的是C++11引入的原子数据类型,使用它程序结果正确,在运行速度上也比加锁的版本快很多。所以,在我们平常写程序的过程中,推荐使用C++11引入的原子变量。

6、什么时候使用原子操作
在多线程并发的条件下,所有不是原子性的操作需要保证原子性时,都需要进行原子操作处理。

例:

long count = 0;

void func()

{

  count++;

}

如果有n个线程同时执行这段代码,所有线程执行完后,count的值不一定等于n。因为count++不是一个原子操作,编译成汇编代码,如下所示:

MOV   eax, [count] 

INC  eax

MOV [count], eax

在cpu执行时 

第一步,先将 count所在内存的值加载到寄存器;

第二步,将寄存器的值自增1;

第三步,将寄存器中的值写回内存。

所以当第一个线程将count值加载到寄存器,并完成自增1,这时寄存器中的值为2,如果此时cpu调度将此线程中断,并执行完其它线程后,再将此线程调度执行,此时,会将2写入到count。count最后的值就成了2。如果要确保改结果正确,那么cout++就要使用原子操作类型。

上述示例中,count的操作如果为count = count + 2,那么也需要原子操作,而如果为count=2或者count==2,则不需要原子操作,因为它们本身的操作就是具有原子性的。

参考:

http://www.cplusplus.com/reference/atomic/atomic/?kw=atomic

https://blog.csdn.net/zhangqhn/article/details/80876177

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值