自旋锁
本文最后更新于:2021年10月19日 下午
自旋锁
1.提出背景:
由于多处理器环境中,某些资源的有限性,对于多个线程共享的资源进行访问时,需要进行互斥访问,便需要引入锁的概念,只有获取了锁的线程才可以对资源进行访问,由于多线程的核心是CPU的时间分片,所以同一时刻只能有一个线程获取锁。
当锁没有被占用时,尝试获取锁的线程成功获取到锁,之后若有另外的线程尝试获取锁,通常有两种处理方式:
1.没有获取到锁的线程就一直循环等待判断该资源是否已经释放锁,这种锁叫做自旋锁。
2.没有获取到锁的线程将自己阻塞起来,等待重新调度请求,这种锁叫互斥锁。
2.自旋锁的定义
自旋锁的定义:当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)
。
3.自旋锁的原理
如果持有锁的线程能在短时间内释放资源,那么那些等待竞争锁的线程就不需要进行内核态和用户态之间的切换进入阻塞状态,只需要等待锁的释放(自旋一小段时间),等到持有锁的线程释放锁之后即可获取到锁,这样做的好处是避免了用户进程和内核态切换的消耗。
自旋锁适用于临界区代码执行时间较短的情况,其他竞争线程只需要等待一小会儿便可以获取到锁,而不用在操作系统的调度下进入阻塞状态然后再切换回来,减少了上下文切换的次数,因此获取自旋锁的进程实际上一直处于用户态,并未进行用户态与内核态的切换。但如果获取锁的线程需要长时间的占用锁,则此时使用自旋锁的效率并不高,大量线程竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起的消耗,其他需要CPU的线程有不能获取到CPU,会造成CPU的浪费。
自旋锁广泛的应用于操作系统内核,是一种轻量级的互斥锁,但是未申请到锁的线程不会被挂起,而是进入忙等待状态。
重点:自旋锁在使用是会自动禁止抢占,这也是为什么不会被操作系统调度的原因,因此如果一个线程一直在自旋等待锁被释放,那么这段时间他还是一直在占用CPU,其他的线程无法抢占他的CPU。
4.使用自旋锁的注意事项
不应用于长时间加锁
被自旋锁保护的临界区不能调用引起睡眠和阻塞的API函数,否则会造成死锁:
如果线程 A 在持有锁期间进入了休眠状态,那么线程 A 会自动放弃 CPU 使用权。线程 B 开始运行,线程 B 也想要获取锁,但是此时锁被 A 线程持有,而且内核抢占还被禁止了!线程 B 无法被调度出去,那么线程 A 就无法运行,锁也就无法释放,好了,死锁发生了!
不能递归申请自旋锁,因为一旦通过递归的方式申请一个你正在持有的锁,那么你就必须“自旋”,等待锁被释放,然而你正处于“自旋”状态,根本没法释放锁。结果就是自己把自己锁死了!
在获取锁之前一定要进行之本地中断(当前CPU中断)
中断中可以使用自旋锁,但是在进入自旋锁的临界区后,又发生了中断,此时新的中断尝试获取锁,但锁正在被原中断持有,现中断无法获取锁,原中断又被打断,无法释放锁,则会造成死锁。
5.具体实现例子待补充……..
6.参考链接(纯搬砖)
本文作者: ziyikee
本文链接: https://ziyikee.fun/2021/10/18/%E8%87%AA%E6%97%8B%E9%94%81/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!