为什么只能使用共享锁保存一个可升级锁

时间:2016-07-29 04:30:53

标签: c++ multithreading c++11 boost mutex

The boost documentation for upgradable and shared locks表示当持有共享锁时,只有一个其他线程可能获得可升级的锁。因此,如果其他线程尝试获取可升级锁,当共享锁与可升级锁一起保存时,它们将阻止。

当多个线程获得可升级的锁以及一个(或多个共享锁)时,是否存在一些我失踪的死锁可能性?或者这只是一个逻辑要求(所以“不应该这样做”)?

请注意,我不是在谈论完全锁定状态。只有可升级的锁定状态。如果可升级锁与其他共享锁一起保存,则它本质上是一个READ锁。那么为什么两个可升级的锁不能连在一起呢?

1 个答案:

答案 0 :(得分:4)

  

当多个线程获得可升级的锁时,是否存在一些我失踪的死锁可能性

TL; DR是的,有。

  

以及一个(或多个共享锁)

这并不会真正影响死锁的可能性。与独占锁相比,允许可升级锁存在时的共享锁只是可升级锁的一项功能。

让我们首先考虑可以使用哪些可升级锁。我们将想象以下情况:

  • 多个写入程序线程必须检查条件(读取操作),然后根据具体情况修改状态
  • 检查条件是否昂贵
  • 很少满足条件。
  • 其他线程也会阅读状态。

现在,让我们考虑一下我们只有读取器(共享)/编写器(独占)锁,没有可升级的锁:

  1. Writer采用独占锁定,并开始检查条件
  2. 其他线程必须在昂贵的检查操作运行时阻塞 - 即使它们只需要阅读。
  3. 检查 - 写入周期的读取部分甚至阻止读取线程可能被认为是一个缺点。那么,让我们考虑一个替代方案:

    1. Writer采用共享锁,并开始检查条件
    2. 其他线程也可以采用共享锁
    3. Writer已检查条件,并释放读锁定
    4. 条件得到满足,而作家现在试图采取独占锁定来继续
    5. 在3和4之间。不止一个作家可能已经完成了对状态的检查 - 因为他们可以同时检查 - 并且现在正在争夺独占锁。只有一个可以赢,其他人必须阻止。当他们阻止时,获胜者正在改变状态。

      在这种情况下,等待获取独占锁的编写者不能认为他们检查的条件已经有效了!另一位作家可能已经抓住了他们之前的锁,现在状态已被修改。他们可以忽略这一点,这可能会导致未定义的行为,具体取决于与条件和修改的关系。或者,当他们获得独占锁时,他们可以再次检查条件,这将使我们恢复到第一种方法,除非由于种族而可能无效的检查是无用的。无论哪种方式,这种方法都比第一种更差。

      上述情况的解决方案是特权读取(可升级)锁定:

      1. Writer获取特权锁,并开始检查条件
      2. 其他线程可能需要共享锁
      3. Writer已检查满足的条件,并升级到独占锁(必须阻止直到其他锁被释放)
      4. 让我们考虑一下多个编写者被授予特权锁的情况。他们同时检查昂贵的情况,这很好,但他们仍然有竞争升级锁。这次,比赛导致死锁,因为每个编写者都持有读锁,并等待所有读锁在升级之前被释放。

        如果可升级锁是与其他可升级锁相关的独占锁,则不会发生此死锁,并且在昂贵的检查和修改之间不存在竞争,但读取器线程仍然可以在写入器时运行检查。