我想知道互斥锁(或其他锁定实现)如何实现锁定函数的wait functionallity。我的意思是,对于mutex.lock调用进行排队的cpu指令是仅在OS中实现的还是什么?
在我做的测试中,我认为这个等待功能只在OS层完成,并且创建了某种旋转,检查是否可以继续锁定,如果没有让线程进入休眠状态。是吗?
非常感谢。
答案 0 :(得分:5)
它取决于平台。通常,如果达到固定的旋转限制,则存在旋转锁定部分,该旋转锁定部分回退到操作系统中的阻塞。
螺旋锁通常通过在互斥锁解锁时读取包含特定值的内存地址来实现。如果它被视为未锁定,则尝试将该值从解锁值原子地更改为锁定值。如果原子交换成功,则互斥锁被锁定。通常会计算旋转次数,如果达到限制,我们会在操作系统中切换到阻止。
OS中的块通常以相同的方式实现,除了不是休眠,线程将其自身添加到等待锁定的事物列表中。当一个线程释放锁时,它会检查操作系统中是否有任何等待,如果是,则解除阻塞。这会导致操作系统安排该线程。它通常会尝试执行与自旋锁相同的原子交换,如果失败则再次在操作系统中阻塞。
在伪代码中:
<强>锁定强>:
<强>解锁强>:
请注意,操作系统必须实现某种机制,以避免在线程设置阻塞之前发生取消阻止在OS中等待的任何线程的请求的竞争。该方法因操作系统而异。例如,Linux有一种称为“futex”的东西,它本质上是一种以原子方式实现锁定伪代码的第4步,第5步和第6步的方法。
警告:如果您尝试在代码中实现此算法,请了解您可能会生成一个与正确实现不同的玩具。您需要深入的,平台特定的知识,以避免令人讨厌的性能吸引陷阱。例如,很容易对自旋锁进行编码,以便从使用超线程共享CPU中的物理内核的另一个线程中窃取核心执行资源。并且很容易编码成功的交换,以便CPU的分支预测预测它会失败,并且当你获得锁定时你会受到可怕的分支错误预测惩罚。
答案 1 :(得分:2)
它将here解释为:
等待怎么样?
现在是棘手的部分。好吧,只是在某种程度上是棘手的,换句话说它很简单。上面的测试和设置机制不支持线程等待值(除了CPU密集的自旋锁)。 CPU并不真正了解高级线程和进程,因此它无法实现等待。操作系统必须提供等待功能。
为了让CPU正常等待,调用者需要进行系统调用。它是唯一可以同步各种线程并提供等待功能的东西。因此,如果我们必须等待互斥锁或释放等待的互斥锁,我们别无选择,只能调用操作系统。大多数操作系统都内置了互斥原语。在某些情况下,它们提供了完整的互斥体。因此,如果系统调用确实提供了完整的互斥量,为什么我们会在用户空间中进行任何类型的测试和设置呢?答案是系统调用有很大的开销,应该尽可能避免。
此时各种操作系统大相径庭,并且随着时间的推移可能会发生变化。在linux下有一个系统调用futex,它提供类似语义的互斥。它经过专门设计,可以在用户空间中完全解决非争用情况。然后将争用案例委托给操作系统以便以安全的方式处理,尽管成本更高。然后等待作为OS进程调度程序的一部分进行处理。