自旋锁、互斥锁

偏向锁->轻量级锁->重量级锁
其中轻量级锁就是自旋的加锁过程。


内核锁:基于内核对象构造的锁机制,就是通常说的内核构造模式。

优点:cpu利用最大化。它发现资源被锁住,请求就排队等候。线程切换到别处干活,直到接受到可用信号,线程再切回来继续处理请求。
缺点:托管代码->用户模式代码->内核代码损耗、线程上下文切换损耗。

在锁的时间比较短时,系统频繁忙于休眠、切换,是个很大的性能损耗。


自旋锁:原子操作+自循环。通常说的用户构造模式。 线程不休眠,一直循环尝试对资源访问,直到可用。

优点:完美解决内核锁的缺点。
缺点:长时间一直循环会导致cpu的白白浪费,高并发竞争下、CPU的消耗特别严重。


混合锁:内核锁+自旋锁。 混合锁是先自旋锁一段时间或自旋多少次,再转成内核锁。

优点:内核锁和自旋锁的折中方案,利用前二者优点,避免出现极端情况(自旋时间过长,内核锁时间过短)。
缺点: 自旋多少时间、自旋多少次,这些策略很难把控。


下面摘录一些关于C++

当中自旋锁(spin lock)与互斥量(mutex)的比较
自旋锁是一种非阻塞锁,也就是说,如果某线程需要获取自旋锁,但该锁已经被其他线程占用时,该线程不会被挂起,而是在不断的消耗CPU的时间,不停的试图获取自旋锁。

互斥量是阻塞锁,当某线程无法获取互斥量时,该线程会被直接挂起,该线程不再消耗CPU时间,当其他线程释放互斥量后,操作系统会激活那个被挂起的线程,让其投入运行。

两种锁适用于不同场景:
如果是多核处理器,如果预计线程等待锁的时间很短,短到比线程两次上下文切换时间要少的情况下,使用自旋锁是划算的。

如果是多核处理器,如果预计线程等待锁的时间较长,至少比两次线程上下文切换的时间要长,建议使用互斥量。

如果是单核处理器,一般建议不要使用自旋锁。因为,在同一时间只有一个线程是处在运行状态,那如果运行线程发现无法获取锁,只能等待解锁,但因为自身不挂起,所以那个获取到锁的线程没有办法进入运行状态,只能等到运行线程把操作系统分给它的时间片用完,才能有机会被调度。这种情况下使用自旋锁的代价很高。

如果加锁的代码经常被调用,但竞争情况很少发生时,应该优先考虑使用自旋锁,自旋锁的开销比较小,互斥量的开销较大。

说的更通俗一点:
如果某个资源在上锁后,取得锁的线程需要做较为复杂的一系列操作(于是需要耗用更多时钟周期)、然后才能释放锁,那么其它线程就应该用非自旋锁交出执行权。
相反,如果可以通过算法优化,使得取得锁的线程只需执行很少几条指令就可以释放锁,那么其它线程就可以使用自旋锁(但是,如果程序是在单核处理器上执行,应无条件使用非自旋锁。这是因为压根没有其他CPU资源供其他线程使用,只能等当前线程耗尽自己的时间片)。
事实上,“尽量降低锁的持有时间”是多线程算法设计上的重点和难点所在。只要你清楚知道线程持有锁的时间(精确到时钟周期),自然就知道什么时候该用什么锁了。

以 synchronize关键字为例,synchronize会获取对象的内置锁,而锁竞争是 kernal mode下的,会经过 user mode到 kernal mode 的切换,是比较花时间的。自旋锁出现的原因是人们发现大多数时候锁的占用只会持续很短的时间,甚至低于切换到 kernal mode所花的时间,所以在进入 kernal mode前让线程等待有限的时间(自适应自旋),如果在此时间内能够获取到锁就避免了很多无谓的时间,若不能则再进入kernal mode竞争锁。


自旋锁较互斥锁之类同步机制的优势

2.1 休眠与忙循环

互斥锁得不到锁时,线程会进入休眠,这类同步机制都有一个共性就是 一旦资源被占用都会产生任务切换,任务切换涉及很多东西的(保存原来的上下文,按调度算法选择新的任务,恢复新任务的上下文,还有就是要修改cr3寄存器会导致cache失效)这些都是需要大量时间的,因此用互斥之类来同步一旦涉及到阻塞代价是十分昂贵的。

一个互斥锁来控制2行代码的原子操作,这个时候一个CPU正在执行这个代码,另一个CPU也要进入, 另一个CPU就会产生任务切换。为了短短的两行代码 就进行任务切换执行大量的代码,对系统性能不利,另一个CPU还不如直接有条件的死循环,等待那个CPU把那两行代码执行完。

2.2 自旋过程


当锁被其他线程占有时,获取锁的线程便会进入自旋,不断检测自旋锁的状态。一旦自旋锁被释放,线程便结束自旋,得到自旋锁的线程便可以执行临界区的代码。对于临界区的代码必须短小,否则其他线程会一直受到阻塞,这也是要求锁的持有时间尽量短的原因!

原文链接:
https://www.zhihu.com/question/38857029

pthread中提供的锁有:pthread_mutex_t, pthread_spinlock_t, pthread_rwlock_t。
pthread_mutex_t : 是互斥锁,同一瞬间只能有一个线程能够获取锁,其他线程在等待获取锁的时候会进入休眠状态。因此pthread_mutex_t消耗的CPU资源很小,但是性能不高,因为会引起线程切换。
pthread_spinlock_t : 是自旋锁,同一瞬间也只能有一个线程能够获取锁,不同的是,其他线程在等待获取锁的过程中并不进入睡眠状态,而是在CPU上进入“自旋”等待。自旋锁的性能很高,但是只适合对很小的代码段加锁(或短期持有的锁),自旋锁对CPU的占用相对较高。
pthread_rwlock_t : 是读写锁,同时可以有多个线程获得读锁,同时只允许有一个线程获得写锁。其他线程在等待锁的时候同样会进入睡眠。读写锁在互斥锁的基础上,允许多个线程“读”,在某些场景下能提高性能。
诸如pthread中的pthread_cond_t, pthread_barrier_t, semaphone等,更像是一种同步原语,不属于单纯的锁。



作者:wuxinliulei
链接:https://www.zhihu.com/question/38857029/answer/78480263
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


作者:hswwjp
链接:https://www.jianshu.com/p/f8bed214bebc
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值