Answers:
当您使用常规锁(互斥锁,关键节等)时,操作系统会将线程置于WAIT状态,并通过在同一内核上调度其他线程来抢占该线程。如果等待时间真的很短,则会降低性能,因为您的线程现在必须等待抢占,才能再次接收CPU时间。
此外,内核对象并非在内核的每个状态下都可用,例如在中断处理程序中或分页不可用时等。
自旋锁不会引起先占,而是循环等待(“自旋”),直到另一个内核释放该锁为止。这样可以防止线程丢失其数量并在锁释放后立即继续。自旋锁的简单机制允许内核在几乎任何状态下使用它。
这就是为什么在单核计算机上,自旋锁只是一个“禁用中断”或“引发IRQL”,它完全阻止了线程调度。
自旋锁最终允许内核避免使用“大内核锁”(当内核进入内核并在出口释放时获得的锁),并且对内核基元进行粒度锁定,从而在多核计算机上实现更好的多处理,从而提高性能。
编辑:出现一个问题:“这是否意味着我应该尽可能使用自旋锁?” 我将尝试回答:
正如我提到的那样,自旋锁仅在预期的等待时间短于量子(读取:毫秒)且抢占没有多大意义(例如,内核对象不可用)的地方有用。
如果等待时间未知,或者您处于用户模式,则自旋锁效率不高。在检查自旋锁是否可用时,您在等待的内核上消耗了100%的CPU时间。您可以阻止其他线程在该核心上运行,直到您的数量到期为止。这种情况仅适用于内核级别的短脉冲,而不太可能是用户模式应用程序的选择。
这里有一个关于SO解决的问题:自旋锁,它们有多有用?
假设资源受锁保护,想要访问该资源的线程需要首先获取该锁。如果该锁不可用,则线程可能会反复检查该锁是否已释放。在这段时间内,线程忙于等待,使用CPU检查锁定,但没有做任何有用的工作。这种锁称为自旋锁。
直到满足特定条件为止,这几乎是一个循环:
while(cantGoOn) {};
sleep(0)
它,它将抢占线程,从而扼杀了使用自旋锁的目的。如果需要让步给其他线程,则应该使用常规锁。(我知道您的评论很老,但想阻止其他人将其视为建议)。
while(something != TRUE ){};
// it happend
move_on();
当您认为进入忙碌的等待循环并集中资源而不是在资源锁定时阻塞时更便宜时,您可能想使用自旋锁。
当锁的粒度很细且数量很大时(例如,链表中的每个节点都有一个锁),以及锁的保持时间总是非常短时,旋转将是有益的。通常,在持有自旋锁的同时,应避免阻塞,调用本身可能阻塞的任何事物,一次持有多个自旋锁,进行动态调度的调用(接口和虚拟),将静态调度的调用变为任何代码都不会执行”拥有或分配内存。
同样重要的是,出于性能原因,SpinLock是一种值类型。因此,必须非常小心,不要意外地复制一个SpinLock实例,因为这两个实例(原始实例和副本)将彼此完全独立,这很可能导致应用程序的错误行为。如果必须传递SpinLock实例,则应按引用而不是按值传递它。
好吧,是的-自旋锁(相对于传统的关键部分等)的要点是,它们在某些情况下(多核系统..)提供更好的性能,因为它们不会立即产生线程余量。