【发布时间】:2016-11-15 02:29:33
【问题描述】:
我正在寻找一种在您有两个线程不断尝试以非常高的频率释放和重新获取同一个锁的情况下优雅降级的锁实现。
当然,很明显,在这种情况下,两个线程不会显着并行处理。从理论上讲,最好的结果是先运行整个线程 1,然后运行整个线程 2,而不进行任何切换——因为切换只会在这里产生大量开销。因此,我正在寻找一种锁定实现,它可以通过在切换前保持同一线程运行一段时间而不是不断切换来优雅地处理这种情况。
问题的长版本
我自己很想用“你的程序坏了,不要那样做”来回答这个问题,这里有一些理由说明我们为什么会陷入这种情况。
锁是“单一全局锁”,即非常粗糙的锁。 (它是 PyPy 中的全局解释器锁(GIL),但问题是关于如何做一般来说,如果你有一个 C 程序。)
我们有以下情况:
一直存在争用。在这种情况下这是意料之中的:锁是一个全局锁,大多数线程需要获取它才能继续进行。所以我们预计其中很大一部分正在等待锁定。这些线程中只有一个可以进行。
持有锁的线程有时可能会爆发短暂的释放。一个典型的例子是,如果这个线程重复调用“外部事物”,例如对文件的许多短写入。这些写入中的每一个通常都很快完成。锁仍然必须释放,以防万一这个外部事情比预期的花费更长的时间(例如,如果写入实际上需要等待磁盘 I/O),以便在这种情况下另一个线程可以获取锁。
如果我们对锁使用一些标准的互斥锁,那么一旦所有者释放锁,锁通常会切换到另一个线程。但问题是,如果程序运行多个线程,每个线程都想做长时间的短时间释放。程序最终花费大部分时间在 CPU 之间切换锁。
在切换之前运行同一个线程一段时间要快得多,至少只要在很短的时间内释放锁。 (例如,在 Linux/pthread 上,即使有其他等待线程,在获取后立即释放有时会立即重新获取锁;但我们希望在大多数情况下都有这样的结果,而不仅仅是有时。)
当然,一旦锁被释放了更长的时间,那么将锁的所有权转移到不同的线程是一个好主意。
所以我正在寻找有关如何做到这一点的一般想法。我猜它应该已经存在于某个地方——在论文中,还是在某个多线程库中?
作为参考,PyPy 尝试通过轮询来实现类似的东西:锁只是一个全局变量,具有同步的比较和交换,但没有操作系统调用;其中一个等待线程被赋予“窃取者”的角色;该“窃取者”线程每 100 微秒唤醒一次以检查变量。这并不是很糟糕(除了正在运行的线程消耗的 100% 时间之外,它还可能花费 1-2% 的 CPU 时间)。这实际上实现了我在这里所要求的,但问题是这是一个不完全支持更传统的锁情况的 hack:例如,如果线程 1 尝试向线程 2 发送消息并等待回答,两个线程切换平均每个需要 100 微秒 --- 如果消息被快速处理,这将是太多了。
【问题讨论】:
-
为什么自旋锁在你的情况下不起作用?
-
不确定你真正需要什么,但你有没有想过
stdatomic.h? -
嗯,有一个问题你不必解决,它永远不会是“高频”。没有可以按下的神奇按钮,您必须对它进行不同的编程。如今,许多语言运行时都很好地支持反应式编程。你必须在 C 中自己动手。
-
这与人们通常寻找的完全相反。一般来说,高质量的锁具有良好的公平性。你的问题归结为“我用什么样的锁来野蛮地饿死我的一个线程?”。
-
如果我理解正确,您需要一个具有“宽限期”的锁。一旦被释放,除了原主人之外,其他任何人都无法在短时间内抓住它。一旦间隔过去,任何服务员都可以抓住它。这是正确的吗?
标签: c multithreading