为什么不能仅使用满足互斥和死锁自由的“1”共享变量来实现“2”线程的锁



我一直在实践层面上研究并发,因此我也开始从理论上研究它,以深入了解计算机科学的这一领域。

然而,我很难理解以下内容:

为什么2线程的Lock不能仅使用满足互斥和死锁自由的1共享变量来实现?

更普遍地说,为什么n线程锁至少需要n共享变量来满足互斥和死锁自由?

考虑两个线程AB。我看到A必须写入这个变量,以表示它获得了锁。变量可以是布尔值。是因为A在写入变量之前需要读取变量,而这是两个操作吗?(不是原子完成的)

很可能,您正在阅读的内容对平台的功能做出了不再现实的假设。您可能正在考虑这样的情况:CPU没有预取,没有发布的写入,总的读取和存储顺序,没有影响内存可见性或内存操作顺序的编译器优化,也没有字撕裂的风险,但没有像增量或比较交换那样的原子"读取-修改-写入"操作。有了这些假设,真的没有办法用一个变量来实现。

这是一个有趣的理论问题,但几乎没有实际意义。现代CPU确实具有所有这些优化功能——它们预取读取,向缓冲区发布写入,重新排序读取和存储,编译器优化内存选项。对于本机整数类型的对齐操作,字撕裂通常不是问题。但是,更重要的是,现代CPU具有复杂的、高性能的原子操作,如递增、递减、比较交换等等

当您编写同步原语时,该练习是高度特定于平台的。可供您使用的功能组合因平台而异。更重要的是,它们的成本因平台而异,因此即使有许多解决方案,它们也可能不一样好。

最后,你必须深入了解每个基元到底让平台做了什么。例如,在现代英特尔CPU上,有超线程。重要的是,例如,一个等待自旋锁的线程不会让共享物理核心的另一个线程挨饿。这需要深入了解超线程的实际工作方式。同样,编写自旋锁也很容易,这样当您获得锁时,您就可以获得所有预测错误分支的母分支,并在性能最关键的时刻破坏管道。您需要了解分支预测是如何工作的,以及它如何与指令管道交互以避免这个问题。

绝大多数程序员永远不应该编写同步原语并在实际的、真实的代码中使用它们。让它们以可靠的可靠性工作是很困难的,而让它们正常工作要困难得多。最重要的是,不可能轻易衡量他们的表现。(当然,只要你不夸大实验代码的有用性,进行实验就很好。)