什么时候应该使用自旋锁而不是互斥锁?
我认为两者都在做同样的工作,你如何决定使用哪一个同步?
理论
从理论上讲,当一个线程试图locking一个互斥锁并且不成功时,因为互斥锁已经被locking,它将进入hibernate状态,立即允许另一个线程运行。 它会继续睡觉直到被唤醒,一旦这个互斥锁被任何线程locking之前解锁,情况就是如此。 当一个线程试图locking一个自旋锁并且不成功时,它将不断的重新尝试locking它,直到它最终成功。 因此它不会允许另一个线程代替它(然而,当然,一旦当前线程的CPU运行时间量已经被超过,操作系统将强制切换到另一个线程)。
问题
互斥锁的问题在于,将线程挂起并再次唤醒它们都是相当昂贵的操作,它们需要相当多的CPU指令,因此也需要一些时间。 如果现在这个互斥锁只能被locking很短的时间,那么把线程hibernate和再次唤醒的时间可能会超过线程实际睡眠的时间,甚至可能超过线程的时间浪费了不断的投票自旋锁。 另一方面,对自旋锁的轮询将不断浪费CPU时间,如果锁持续较长的时间,则会浪费更多的CPU时间,而如果线程正在hibernate,则会更好。
解决scheme
在单核/单CPU系统上使用自旋锁通常是没有意义的,因为只要自旋锁轮询阻塞唯一可用的CPU核心,其他线程就不能运行,并且由于没有其他线程可以运行,锁不会运行也可以解锁。 IOW,一个螺旋锁只浪费CPU时间在这些系统上没有真正的好处。 如果线程进入睡眠状态,另一个线程可能立即运行,可能会解锁该锁,然后允许第一个线程继续处理,一旦它再次醒来。
在一个多核/多CPU系统上,由于大量的锁只能在很短的时间内保存,所以浪费时间不断地让线程进入hibernate状态,再次唤醒线程可能会显着降低运行时性能。 当使用螺旋锁代替时,线程有机会利用完整的运行时间量(总是只在很短的时间内阻塞,但是立即继续工作),从而导致更高的处理吞吐量。
实践
由于程序员往往不能预先知道互斥锁或自旋锁是否会更好(例如,因为目标体系结构的CPU核心数量未知),操作系统也不知道某个代码是否针对单核或多核环境下,绝大多数系统不会严格区分互斥和自旋锁。 事实上,大多数现代操作系统都具有混合互斥体和混合自旋锁。 这实际上是什么意思?
一个混合互斥体在多核系统上起初就像一个自旋锁。 如果线程无法locking互斥锁,它将不会立即进入睡眠状态,因为互斥锁可能很快就会被解锁,所以互斥量将首先像螺旋锁一样运行。 只有在一段时间(或重试或其他测量因素)后还没有获得locking时,线程才真正进入睡眠状态。 如果相同的代码只在一个只有一个内核的系统上运行,那么这个互斥量就不会自旋,尽pipe如上所述,这并不会有好处。
混合自旋锁起初就像一个正常的自旋锁一样,但是为了避免浪费太多的CPU时间,它可能会有一个回退策略。 它通常不会让线程进入hibernate状态(因为在使用spinlock时不希望发生这种情况),但可能会决定停止线程(立即或在一段时间后),并允许另一个线程运行,从而增加了螺旋锁解锁的机会(一个纯线程切换通常比一个线程睡觉并稍后再次唤醒线程更便宜,尽pipe不是很远)。
概要
如果有疑问,使用互斥锁,它们通常是更好的select,大多数现代系统将允许它们在非常短的时间内旋转,如果这看起来有益的话。 使用自旋锁有时可以提高性能,但是只有在某些条件下,而且你有疑问的事实告诉我,你并没有在螺旋锁有益的任何项目上工作。 你可能会考虑使用自己的“锁对象”,它可以在内部使用自旋锁或互斥锁(例如,创build这样的对象时可以configuration此行为),最初在任何地方使用互斥锁,如果您认为在某处使用自旋锁帮助,试一试并比较结果(例如使用探查器),但在跳到结论之前一定要testing一个单核和多核系统的情况(可能还有不同的操作系统,如果你的代码将是跨平台的)。
继续Mecki的build议,本文在Alexander Sandler的博客Alex on Linux上展示了互斥锁和pthread spinlock ,它们展示了如何使用#ifdef来testing行为的spinlock
和mutexes。
但是,一定要根据你的观察采取最后的呼叫,理解给出的例子是一个孤立的情况下,你的项目要求,环境可能是完全不同的。
另请注意,在某些环境和条件下(例如在调度级别> = 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
麦基的回答很好。 但是,在单个处理器上,当任务正在等待由中断服务例程给出的锁时,使用自旋锁可能是有意义的。 中断会将控制权交给ISR,ISR将为等待任务准备好资源。 在将控制权交给被中断的任务之前,它将通过释放锁来结束。 旋转任务会发现螺旋锁可用并继续。
Spinlock和Mutex同步机制今天很常见。
先考虑一下Spinlock。
基本上这是一个忙碌的等待动作,这意味着我们要等待一个指定的locking之后才能继续下一个动作。 从概念上来说非常简单,但实施并非如此。 例如:如果锁没有被释放,那么线程被换出并进入睡眠状态,我们应该处理它吗? 当两个线程同时请求访问时如何处理同步锁?
通常,最直观的想法是通过variables来处理同步,以保护关键部分。 互斥的概念是相似的,但它们仍然不同。 重点介绍:CPU利用率。 Spinlock消耗CPU时间来等待动作,因此,我们可以总结两者的区别:
在同质多核环境中,如果在临界区花费的时间比使用Spinlock小,因为我们可以减less上下文切换时间。 (单核比较并不重要,因为一些系统在交换机中间实施了Spinlock)
在Windows中,使用Spinlock会将线程升级到DISPATCH_LEVEL,在某些情况下可能不允许使用该线程,所以这次我们不得不使用互斥锁(APC_LEVEL)。
在单核/单CPU系统上使用自旋锁通常是没有意义的,因为只要自旋锁轮询阻塞唯一可用的CPU核心,其他线程就不能运行,并且由于没有其他线程可以运行,锁不会运行也可以解锁。 IOW,一个螺旋锁只浪费CPU时间在这些系统上没有真正的好处
这是错误的。 在单处理器系统上使用自旋锁不会浪费CPU周期,因为一旦一个进程需要一个自旋锁,抢先被禁用,这样就不会有其他人在旋转! 这只是使用它没有任何意义! 因此,Uni系统上的自旋锁在编译时被内核的preempt_disable所取代!