C++11中的原子操作(atomic operation)和自旋锁

1. 原子操作

  所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。
  我们在原子操作之前在多线程同步中防止发生数据竞争的操作是加互斥锁,但互斥锁是操作系统这一层级的,最终映射到CPU上也是一堆指令,是指令就必然会带来额外的开销;既然CPU指令是多线程不可再分的最小单元,那我们如果有办法将代码语句和指令对应起来,不就不需要引入互斥锁从而提高性能了吗? 而这个对应关系就是所谓的原子操作
  在新标准C++11,引入了原子操作的概念,并通过这个新的头文件提供了多种原子操作数据类型,例如,atomic_bool,atomic_int等等,如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率

在这里插入图片描述

2. 自旋锁

  自旋锁是一种基础的同步原语,用于保障对共享数据的互斥访问。与互斥锁的相比,在获取锁失败的时候不会使得线程阻塞而是一直自旋尝试获取锁。当线程等待自旋锁的时候,CPU不能做其他事情,而是一直处于轮询忙等的状态。
  自旋锁主要适用于被持有时间短,线程不希望在重新调度上花过多时间的情况。实际上许多其他类型的锁在底层使用了自旋锁实现,例如多数互斥锁在试图获取锁的时候会先自旋一小段时间,然后才会休眠。如果在持锁时间很长的场景下使用自旋锁,则会导致CPU在这个线程的时间片用尽之前一直消耗在无意义的忙等上,造成计算资源的浪费。

自旋锁相关API:

int pthread_spin_init(pthread_spinlock_t *, int); //初始化自旋锁
int pthread_spin_lock(pthread_spinlock_t *); //获得一个自旋锁 
int pthread_spin_trylock(pthread_spinlock_t *); //尝试获取一个自旋锁
int pthread_spin_unlock(pthread_spinlock_t *);//释放(解锁)一个自旋锁 
int pthread_spin_destroy(pthread_spinlock_t *); //销毁一个自旋锁 

使用自旋锁注意
  由于自旋时不释放CPU,因而持有自旋锁的线程应该尽快释放自旋锁,否则等待该自旋锁的线程会一直在哪里自旋,这就会浪费CPU时间。

持有自旋锁的线程在sleep之前应该释放自旋锁以便其他线程可以获得该自旋锁

3. 原子操作和使用互斥锁和自旋锁的速度对比

  1. 单线程无锁速度最快,但应用场合受限;

  2. 多线程无锁速度第二快,但结果不对,未保护临界代码段;

  3. 多线程原子锁第三快,且结果正确;

  4. 多线程互斥量较慢,慢与原子锁近10倍,结果正确;

  5. 多线程自旋锁最慢,慢与原子锁30倍,结果正确。

结论:原子锁速度最快,互斥量和自旋锁都用保护多线程共享资源。


  自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。
  互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行。

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值