自旋锁 vs 忙等待

21
请解释为什么"繁忙等待(Busy Waiting)"通常不被看好,而"自旋(Spinning)"经常被认为是可以的。 就我所知,它们都会无限循环,直到满足某些条件为止。

1
我认为在自旋锁中,除了等待其释放之外,实际上还会做其他事情... - Paul Stelian
2
除非您有非常好的保证可以在一微秒或更短时间内获得锁,否则请勿使用自旋锁。如果需要更长时间,则会转化为忙等待,并可能出现错误。 - Hans Passant
1
使用忙等待的锁被称为自旋锁。——《现代操作系统》 - walkerlala
3
“Loop infinitely ... until”是一句英文语句,意为“无限循环...直到”。您问“infinite”的意思是什么。该词表示没有界限或限制的数量或程度,即无限的或极大的。 - Kerrek SB
2
这更像是“老鼠对松鼠”的交易:基本上是相同的事情,但具有更好的公关。自旋锁是被吹嘘的忙等待。请谨慎操作。 - Sergey Kalinichenko
显示剩余3条评论
3个回答

16

自旋锁通常在资源争用较低且CPU只需少量迭代即可进行有效工作时使用。然而,锁定功能的库实现通常会使用自旋锁后跟常规锁。如果资源无法在合理的时间内获取,则使用常规锁。这样做是为了减少在通常很快获得锁的情况下上下文切换的开销。

术语忙等待倾向于意味着您愿意旋转并等待硬件寄存器或内存位置的更改。该术语不一定意味着锁定,但它确实暗示着在紧密循环中等待,重复探测更改。

您可能希望使用忙等待来检测您想要立即响应的环境中的某种变化。因此,使用忙等待来实现自旋锁。在某些类型的嵌入式编程中,忙等待非常有用,因为非常低的延迟响应比浪费CPU周期更重要。

与此相关的术语是“无锁”和“无等待”:

所谓的“无锁算法”通常使用紧密的忙等待和CAS指令,但在普通情况下争用是如此之低,以至于CPU通常只需要迭代几次。
所谓的“无等待算法”根本不进行任何忙等待。
(请注意,“无锁”和“无等待”在学术环境中略有不同,详见维基百科关于非阻塞算法的文章。)

总的来说,+1,但有一些挑剔:经典的CAS模式(读取-CAS-变量-计算新值-尝试CAS-如果失败-重复此过程)并不真正合格为忙等待(我们实际上不是在等待某个外部变量改变 - 这可能需要很长时间,而是在等待变量从外部干扰中稳定下来 - 在实践中永远不会超过2-3次迭代); 尽管如此,这种CAS模式仍不符合“无等待” :-(。 - No-Bugs Hare

2
标准互斥锁与自旋锁:当一个线程调用锁定和获取互斥锁的函数时,该函数不会返回直到互斥锁被锁定。这是事件同步的典型情况:线程等待事件,即已经获得互斥锁所有权的事实。这可以通过以下两种方式实现:
• 空闲等待:等待锁定互斥锁的线程被阻塞在等待状态中,如第2章所述。它释放CPU,然后可以用于运行另一个线程。当互斥锁变为可用时,运行时系统唤醒并重新安排等待线程,然后可以锁定现在可用的互斥锁。
• 忙等待,也称为自旋等待,在等待锁定互斥锁的线程不释放CPU。它保持调度状态,执行一些微不足道的无操作指令,直到互斥锁被释放。
标准互斥锁通常采用第一种策略进行空闲等待。但是,一些库还提供了采用自旋等待策略的互斥锁。最佳选择取决于应用程序上下文。对于非常短的等待时间,在用户空间自旋更有效,因为将线程置于阻塞状态需要周期。但是,对于长时间等待,睡眠线程会释放其CPU,从而使周期可用于其他线程。
我从以下来源中找到了相关信息:https://www.sciencedirect.com/topics/computer-science/waiting-thread

-2

当你理解规则的精确原因并具有详细的平台和应用程序知识时,你就知道何时适当地违反该规则。自旋锁是由专家实现的,他们完全了解他们正在开发的平台以及自旋锁的预期应用。

忙等待的问题很多,但在大多数平台上,都有解决方案。问题包括:

  1. 对于具有超线程的CPU,忙等待线程可能会使同一物理核心中的另一个线程饥饿,甚至是它正在等待的线程。
  2. 当你忙等待时,当你最终获得你正在等待的资源时,你会遇到所有错误预测分支中最严重的情况。
  3. 忙等待会干扰CPU电源管理。
  4. 忙等待会使内核总线饱和,因为你不断检查的东西会导致缓存同步流量。

但是设计自旋锁的人员了解所有这些问题,并且知道如何在平台上准确地缓解它们。他们不编写“天真”的自旋代码,而是编写智能自旋代码。

所以,是的,它们都会无限循环,直到满足某些条件,但它们的循环方式是不同的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接