锁定处理高争用,高频率的情况

Arm*_*igo 6 c multithreading

我正在寻找一种锁定实现,在你有两个线程不断尝试以非常高的频率释放和重新获取相同的锁的情况下,它会优雅地降级.

当然很明显,在这种情况下,两个线程并不会显着地并行进行.从理论上讲,最好的结果是通过运行整个线程1,然后是整个线程2来实现,而不需要任何切换---因为切换只会在这里产生大量开销.所以我正在寻找一种锁定实现,通过在切换之前保持相同的线程运行一段时间而不是不断切换来优雅地处理这种情况.

长版的问题

因为我自己很想通过"你的程序被打破,不要那样做"来回答这个问题,这里有一些理由说明为什么我们最终会遇到这种情况.

锁是一个"单一全局锁",即一个非常粗略的锁.(它是PyPy中的全局解释器锁(GIL),但问题是关于如何一般地执行它,比如说你有一个C程序.)

我们有以下情况:

  • 经常存在争议.在这种情况下,这是预期的:锁是一个全局锁,需要获取大多数线程才能进行.所以我们希望他们中的很大一部分都在等待锁定.这些线程中只有一个可以进展.

  • 持有锁的线程有时会爆发短暂的释放.一个典型的例子是,如果这个线程重复调用"外部的东西",例如对文件的许多短写.这些写入中的每一个通常都很快完成.锁定仍然必须被释放,以防这个外部事件花费比预期更长的时间(例如,如果写入实际上需要等待磁盘I/O),以便在这种情况下另一个线程可以获取锁定.

如果我们使用一些标准互斥锁进行锁定,那么一旦所有者释放锁定,锁定通常会切换到另一个线程.但问题是,如果程序运行多个线程,每个线程都想要做一个长期的短期发布.该程序最终花费大部分时间在CPU之间切换锁定.

在切换之前运行相同的线程一段时间要快得多,至少只要锁被释放很短的时间.(例如在Linux/pthread上,即使有其他等待线程,有时会立即重新获取锁定,但是我们在大多数情况下都会喜欢这种结果,而不仅仅是有时.)

当然,一旦锁被释放更长的时间,那么将锁的所有权转移到另一个线程就成了一个好主意.

所以我正在寻找关于如何做到这一点的一般想法.我想它应该存在于某个地方---在一篇论文中,还是在一些多线程库中?

作为参考,PyPy试图通过轮询来实现这样的东西:锁只是一个全局变量,具有同步的比较和交换但没有OS调用; 其中一个等待线程被赋予"窃取者"的角色; "stealer"线程每100微秒唤醒一次以检查变量.这并不是非常糟糕(除了正在运行的线程消耗的100%之外,它的成本可能是CPU时间的1-2%).这实际上实现了我在这里要求的东西,但问题是这是一个不干净地支持更传统的锁的情况的hack:例如,如果线程1试图向线程2发送消息并等待回答,两个线程切换每个平均需要100微秒 - 如果快速处理消息,则太多了.

ran*_*ngo -1

自旋锁可能在你的情况下工作得更好。它们避免了上下文切换,并且如果线程可能仅在短时间内持有锁,那么它们的效率很高。

正因为如此,它们被广泛用于操作系统内核中。