信号量和自旋锁之间的基本区别是什么?
我们什么时候会使用信号量而不是自旋锁?
发布于 2013-06-21 02:54:22
自旋锁和信号量的主要区别在于四个方面:
1.它们是什么
自旋锁是锁的一种可能的实现,即通过忙等待(“自旋”)实现的锁。信号量是锁的泛化(或者,反过来,锁是信号量的特例)。通常情况下,自旋锁仅在一个进程内有效,而信号量也可用于不同进程之间的同步。
锁的作用是互斥的,即一次只有一个线程可以获得锁并继续执行“临界区”的代码。通常,这意味着修改由多个线程共享的某些数据的代码。
信号量有一个计数器,它允许自己被一个或多个线程获取,这取决于您发送给它的值,以及(在某些实现中)它的最大允许值。
在某种程度上,可以认为锁是最大值为1的信号量的特例。
2. What they do
如上所述,自旋锁是一个锁,因此是一种互斥(严格的1: 1)机制。它通过重复查询和/或修改内存位置来工作,通常以原子方式进行。这意味着获取自旋锁是一个“繁忙”的操作,可能会在很长一段时间内(可能永远)消耗CPU周期。而它实际上是“一无所获”。
这种方法的主要动机是,上下文切换的开销相当于旋转几百次(或数千次),所以如果可以通过燃烧几个旋转周期来获得锁,总体上可能会更有效。此外,对于实时应用程序,阻塞并等待调度器在将来某个遥远的时间返回到它们可能是不可接受的。
相比之下,信号量要么根本不旋转,要么只旋转很短的时间(作为避免syscall开销的优化)。如果无法获取信号量,它就会阻塞,从而将CPU时间留给另一个准备运行的线程。当然,这可能意味着在重新调度线程之前有几毫秒的时间,但如果这没有问题(通常不是),那么它可能是一种非常有效的、节省CPU的方法。
3.它们在出现拥塞时的行为
一种常见的误解是,自旋锁或无锁算法“通常更快”,或者它们只适用于“非常短的任务”(理想情况下,任何同步对象都不应该持有超过绝对必要的时间)。
一个重要的区别是不同的方法在拥塞情况下的表现。
一个设计良好的系统通常拥塞很低或没有拥塞(这意味着不是所有线程都会同时尝试获取锁)。例如,通常不会编写获取锁的代码,然后从网络加载0.5兆字节的zip压缩数据,对数据进行解码和解析,最后修改共享引用(将数据附加到容器等)。在解锁之前。取而代之的是,只有在访问共享资源的目的时才会获得锁。
由于这意味着临界区之外的工作比临界区内的工作多得多,线程在临界区内的可能性自然相对较低,因此几乎没有线程同时争用锁。当然,有时会有两个线程同时尝试获取锁(如果不可能发生这种情况,您就不需要锁了!),但这是“健康”系统中的例外,而不是规则。
在这种情况下,自旋锁的性能大大优于信号量,因为如果没有锁拥塞,则获取自旋锁的开销只有十几个周期,而上下文切换需要数百/数千个周期,或者丢失剩余时间片需要1000-2000万个周期。
另一方面,如果拥塞很高,或者如果锁被长时间持有(有时你就是情不自禁!),自旋锁将消耗大量的CPU周期而一无所获。
在这种情况下,信号量(或互斥)是更好的选择,因为它允许不同的线程在这段时间内运行有用的任务。或者,如果没有其他线程可以做一些有用的事情,它允许操作系统降低CPU的速度,减少热量/节约能源。
此外,在单核系统上,当存在锁拥塞时,自旋锁的效率将非常低,因为旋转线程将浪费其完整的时间等待不可能发生的状态更改(直到释放线程被调度,而等待线程正在运行时不会发生状态更改!)。因此,给定任何数量的争用,获取锁在最好的情况下需要大约1 1/2个时间片(假设释放的线程是下一个被调度的线程),这不是很好的行为。
4.它们是如何实现的
信号量现在通常会在Linux下包装sys_futex (可选地使用一个在几次尝试后退出的自旋锁)。
自旋锁通常是使用原子操作实现的,而不使用操作系统提供的任何东西。在过去,这意味着要么使用编译器内部函数,要么使用不可移植的汇编指令。同时,C++11和C11都将原子操作作为语言的一部分,因此除了编写可证明正确的无锁代码的一般困难之外,现在可以以完全可移植和(几乎)无痛苦的方式实现无锁代码。
发布于 2008-10-12 19:06:03
很简单,信号量是一个“让步”的同步对象,自旋锁是一个“忙碌的等待”对象。(信号量还有更多的功能,因为它们可以同步多个线程,而不像互斥、守卫、监视器或者保护代码区域不受单个线程影响的临界区)
你会在更多的情况下使用信号量,但在你要锁定很短时间的地方使用自旋锁-锁定是有成本的,特别是当你锁得很多的时候。在这种情况下,等待受保护的资源解锁一小段时间会更有效。显然,如果你旋转太长时间,性能会受到影响。
通常,如果旋转时间超过线程量程,则应该使用信号量。
发布于 2008-10-12 19:53:02
除了Yoav Aviram和gbjbaanb所说的之外,另一个关键点是,你永远不会在单CPU机器上使用自旋锁,而信号量在这样的机器上是有意义的。现在,您经常很难找到一台没有多核、超线程或等效的机器,但在只有一个CPU的情况下,您应该使用信号量。(我相信原因是显而易见的。如果单个CPU忙于等待其他东西来释放自旋锁,但它在唯一的CPU上运行,则在当前进程或线程被O/S抢占之前,锁不太可能被释放,这可能需要一段时间,并且在抢占发生之前不会发生任何有用的事情。)
https://stackoverflow.com/questions/195853
复制相似问题