Mec*_*cki 688
理论
理论上,当一个线程试图锁定互斥锁并且它没有成功时,因为互斥锁已经被锁定,它将进入休眠状态,立即允许另一个线程运行.它将继续睡眠直到被唤醒,一旦互斥锁被之前持有锁的任何线程解锁,就会出现这种情况.当一个线程试图锁定一个自旋锁并且它没有成功时,它将不断重新尝试锁定它,直到它最终成功; 因此,它不允许另一个线程取代它(但是,一旦超过当前线程的CPU运行时量,操作系统将强制切换到另一个线程).
问题
互斥锁的问题在于将线程置于休眠状态并再次唤醒它们都是相当昂贵的操作,它们需要相当多的CPU指令,因此也需要一些时间.如果现在互斥锁只被锁定了很短的时间,那么将线程置于休眠状态并再次唤醒它所花费的时间可能会超过线程实际睡眠的时间,甚至可能超过线程的时间.通过不断轮询螺旋锁来浪费.另一方面,对自旋锁进行轮询将不断浪费CPU时间,如果锁被保持更长时间,这将浪费更多的CPU时间,如果线程正在休眠则会好得多.
解决方案
在单核/单CPU系统上使用自旋锁通常没有意义,因为只要自旋锁轮询阻止唯一可用的CPU核心,没有其他线程可以运行,并且由于没有其他线程可以运行,锁将不会也可以解锁.IOW,一个自旋锁在这些系统上只浪费CPU时间而没有真正的好处.如果线程被置于休眠状态,则另一个线程可以立即运行,可能解锁锁定,然后在第一个线程再次唤醒后允许第一个线程继续处理.
在多核/多CPU系统上,只有很长时间保持的锁定,浪费时间不断地让线程进入休眠状态并再次唤醒它们可能会显着降低运行时性能.当使用自旋锁时,线程有机会利用其完整的运行时量程(总是只在很短的时间内阻塞,但随后立即继续工作),从而导致更高的处理吞吐量.
实践
由于程序员通常不能事先知道互斥锁或自旋锁是否会更好(例如,因为目标体系结构的CPU核心数量未知),操作系统也不知道某段代码是否针对单核或在多核环境中,大多数系统都没有严格区分互斥锁和自旋锁.事实上,大多数现代操作系统都有混合互斥锁和混合自旋锁.这究竟意味着什么?
混合互斥体在多核系统上的行为类似于自旋锁.如果一个线程无法锁定互斥锁,它将不会立即进入休眠状态,因为互斥锁可能会很快解锁,因此互斥锁将首先表现得像一个自旋锁.只有在经过一定时间(或重试或任何其他测量因素)后仍未获得锁定时,线程才真正进入休眠状态.如果相同的代码在只有一个核心的系统上运行,那么互斥锁将不会自动锁定,但是,如上所述,这将是无益的.
混合自旋锁首先表现得像普通的自旋锁,但为了避免浪费太多的CPU时间,它可能有一个退避策略.它通常不会让线程进入休眠状态(因为你不希望在使用自旋锁时发生这种情况),但它可能决定停止线程(立即或在一定时间之后)并允许另一个线程运行因此增加了自旋锁被解锁的机会(纯线程开关通常比让线程进入睡眠并稍后再次唤醒它的那个更便宜,但不是很远).
摘要
如果有疑问,使用互斥锁,它们通常是更好的选择,大多数现代系统将允许它们在非常短的时间内旋转锁定,如果这似乎是有益的.使用自旋锁有时可以提高性能,但只有在某些条件下,而且你有疑问的事实告诉我,你没有在任何螺旋锁可能有益的项目上工作.您可以考虑使用自己的"锁定对象",可以在内部使用自旋锁或互斥锁(例如,在创建此类对象时可以配置此行为),最初在任何地方使用互斥锁,如果您认为在某处使用自旋锁可能真的帮助,试一试并比较结果(例如使用分析器),但在你得出结论之前一定要测试这两种情况,一个单核和一个多核系统(如果你的代码,可能还有不同的操作系统)将跨平台).
与Mecki的建议继续,本文并行线程互斥VS并行线程自旋锁亚历山大·桑德勒的博客,亚历克斯在Linux上显示了如何spinlock和mutexes可以实现用#ifdef对其进行测试的行为.
但是,请务必根据您的观察结果进行最后的调用,理解为给出的示例是一个孤立的案例,您的项目要求,环境可能完全不同.
小智 6
另请注意,在某些环境和条件下(例如在调度级别上运行Windows> = DISPATCH LEVEL),您不能使用互斥锁,而是使用自旋锁.在unix上 - 同样的事情.
以下是竞争对手stackexchange unix网站上的同等问题:https: //unix.stackexchange.com/questions/5107/why-are-spin-locks-good-choices-in-linux-kernel-design-instead-of-something-更多
有关在Windows系统上调度的信息:http: //download.microsoft.com/download/e/b/a/eba1050f-a31d-436b-9281-92cdfeae4b45/IRQL_thread.doc
小智 6
梅基的答案很好。但是,在单个处理器上,当任务正在等待中断服务例程提供的锁时,使用自旋锁可能很有意义。中断会将控制权转移给ISR,ISR将为等待任务使用的资源做好准备。在将控制权交还给被中断的任务之前,它会先释放锁定。旋转任务将找到可用的自旋锁并继续。
自旋锁和互斥同步机制如今非常常见。
我们先来考虑一下 Spinlock。
基本上,这是一个繁忙的等待操作,这意味着我们必须等待指定的锁被释放才能继续进行下一个操作。概念上很简单,但实现起来却不是那么回事。例如:如果锁没有被释放,那么线程被换出并进入睡眠状态,我们应该如何处理?当两个线程同时请求访问时如何处理同步锁?
一般来说,最直观的想法是通过变量处理同步来保护临界区。Mutex的概念很相似,但还是有区别的。重点关注:CPU 利用率。自旋锁会消耗CPU时间来等待执行操作,因此,我们可以总结两者之间的区别:
在同构多核环境中,如果临界区花费的时间较小,则可以使用Spinlock,因为可以减少上下文切换时间。(单核比较并不重要,因为有些系统在中间切换实现了Spinlock)
在Windows中,使用Spinlock会将线程升级到DISPATCH_LEVEL,这在某些情况下可能是不允许的,所以这次我们不得不使用Mutex(APC_LEVEL)。