“旋转锁”到底是什么?

我一直想知道它们是什么: 每次我听到它们的时候,未来的飞轮一样的设备的图像就会跳舞(滚动?)在我脑海里。

他们是什么?

89988 次浏览

它是一个循环,一直旋转直到满足一个条件。

 while(something != TRUE ){};
// it happend
move_on();

它基本上就是一个循环,一直循环到满足某个条件为止:

while(cantGoOn) {};

假设一个资源受到一个锁的保护,想要访问该资源的线程需要首先获取该锁。如果锁不可用,线程可能会重复检查锁是否已被释放。在此期间,线程忙于等待,检查锁,使用 CPU,但不做任何有用的工作。这种锁称为旋转锁。

在自旋锁中,线程等待锁可用。这通常被用来避免在一个很小的时间段内获取内核对象的范围内获取内核对象的开销。

例如:

While(SpinCount-- && Kernel Object is not free)
{}


try acquiring Kernel object

简而言之,自旋锁使用原子比较和交换(CAS)或类似测试和设置的指令来实现无锁、无等待线程安全习惯用法。这种结构适用于多核机器。

嗯,是的——自旋锁(相对于传统的关键部分等)的关键点在于它们在某些情况下(多核系统)提供了更好的性能。.)因为它们不会立即产生线程的其余量子。

当您使用常规锁(互斥锁、关键部分等)时,操作系统通过在同一核心上调度其他线程,将您的线程置于 WAIT 状态,并将其置于 先发制人状态。如果等待时间真的很短,这会降低性能,因为您的线程现在必须等待抢占再次接收 CPU 时间。

此外,内核对象并不是在内核的每个状态都可用的,比如在 interrupt handler 中,或者当分页不可用时,等等。

自旋锁不会导致抢占,而是在循环中等待(“自旋”) ,直到另一个核释放锁。这样可以防止线程丢失其 量子,并在锁释放后立即继续执行。自旋锁的简单机制允许内核在几乎任何状态下使用它。

这就是为什么在单个核心机器上,自旋锁只是一个“禁用中断”或“引发 IRQL”,它可以完全阻止线程调度。

自旋锁最终允许内核避免“大内核锁”(当内核进入内核并在出口处释放时获得的锁) ,并对内核原语进行粒度锁定,从而在多核机器上实现更好的多处理,从而获得更好的性能。

编辑 : 出现了一个问题: “这是否意味着我应该在任何可能的地方使用自旋锁?”我将试图回答这个问题:

正如我提到的,自旋锁只有在预期等待时间比量程短(读为毫秒)和抢占没有多大意义(例如内核对象不可用)的情况下才有用。

如果等待时间是未知的,或者如果您处于用户模式,自旋锁是无效的。在检查自旋锁是否可用的时候,您在等待的核心上消耗了100% 的 CPU 时间。您阻止其他线程在该内核上运行,直到您的量子到期。这种情况只适用于内核级别的短时爆发,不太可能适用于用户模式应用程序。

这里有一个关于如何解决这个问题的问题: 旋转锁,它们有什么用?

这是一种能锁定 忙着等待的锁

它被认为是一种反模式,除了非常低级的驱动程序编程(在这种情况下,调用一个“适当的”等待函数比仅仅忙碌锁定几个周期的开销更大)。

参见例子 Linux 内核中的自旋锁

当您认为进入一个繁忙的等待循环并池资源而不是在资源被锁定时阻塞成本更低时,可以使用自旋锁。

当锁具有细粒度且数量大(例如,链表中的每个节点都有一个锁)以及锁保持时间总是非常短时,旋转可能是有益的。一般来说,当持有一个自旋锁时,应该避免阻塞,调用任何可能被阻塞的东西,同时持有多个自旋锁,进行动态分派调用(接口和虚拟) ,对任何不属于自己的代码进行静态分派调用,或者分配内存。

出于性能原因,还需要注意的是,SpinLock 是一种值类型。因此,必须非常小心,不要意外地复制一个 SpinLock 实例,因为这两个实例(原始实例和副本)将完全独立于另一个实例,这可能会导致应用程序的错误行为。如果必须传递 SpinLock 实例,则应该通过引用而不是通过值传递。

自旋锁,是一种非阻塞型和非睡眠型的锁。任何想要为任何共享或关键资源获取自旋锁的线程都会不断地自旋,浪费 CPU 处理周期,直到它获取指定资源的锁为止。一旦获得了自旋锁,它就会尝试完成其量程中的工作,然后分别释放资源。自旋锁是优先级最高的锁类型,简单地说,就是非抢占式的锁类型。