作者| 慕课网精英讲师 咚咚呛
最近总有同学问我:
对自旋锁的介绍完全听不懂。我猜,这是一种线程的锁定,直到这个线程不用这个资源了,才会彻底解锁,让出线程。但是希望得到严谨的解答,谢谢。
这个问题要从自旋锁的实现去回答。自旋锁是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待,也即是名字中“自旋”本身的含义。
自旋锁在不同语言都有不同的实现,但核心逻辑都是一样的,你可以看做是一个死循环去判断锁变量是否可用,如果可用则跳出循环,否则继续死循环。
逻辑如下:
void spin_lock(lock)
{
while (test_and_set(lock, true));// 锁可用则返回,否则继续循环
}
生产者每次加一前后都会加锁和解锁,那解锁后,锁变量可用,消费者线程就有机会进行减一操作了。那不就是和互斥锁一样了吗?
是的,就锁的使用上,自旋锁的使用方式和互斥锁的使用并无太大区别,但需要注意的是,自旋锁等待的过程是100%一个核的CPU的,也即是不会让出CPU,这一点和其他锁不同。这一点在课程中,有详细的演示。
既然这种锁会100%占用CPU,那为什么计算机需要有自旋锁这种锁,对计算机的性能有什么好处呢?
这是好问题,为什么计算机内部的实现需要自旋锁,这需要联系前面学习的知识。通过前面的学习我们知道,进程或者线程在运行的时候,是有上下文的,当不同线程进行切换的时候,为了让线程可以运行起来,需要很多的准备工作,这个准备工作,我们称为“上下文切换”。在CPU的一个核中,每秒钟可会进行万级~十万级别的上下文切换次数,每次上下文切换都需要一定的成本,因此频繁的上下文切换会对计算机性能造成较大的影响。锁的好处就是它只是忙等待,线程始终在运行,相比互斥锁的使用,避免了上下文切换。所以这也限定了互斥锁的使用范围,如果互斥锁等待的时间过长,那么由于它本身对CPU的占用,会导致别的线程无法使用CPU。因此,互斥锁适用于可预见等待时间很短的多线程同步场景,而对于等待时间不可预测或者等待时间很长的场景,仍然是互斥锁具备更高的效率。