为什么共享锁只能持有一个可升级锁

Cur*_*ous 5 c++ multithreading boost mutex c++11

可升级和共享锁的 boost 文档说,当共享锁被持有时,只有一个其他线程可以获得可升级锁。因此,如果其他线程在共享锁与可升级锁一起持有时尝试获取可升级锁,它们将被阻塞。

当多个线程获取可升级锁以及一个(或多个共享锁)时,是否存在某种死锁可能性?或者这只是一个合乎逻辑的要求(所以“不应该这样做”之类的事情)?

请注意,我不是在谈论专门的锁定状态。只有可升级的锁定状态。如果一个可升级锁与其他共享锁一起持有,它本质上就是一个 READ 锁。那为什么不能将两个可升级的锁放在一起呢?

eer*_*ika 5

当多个线程获取可升级锁时,是否存在我遗漏的死锁可能性

TL;DR 是的,有。

连同一个(或多于一个共享锁)

这并不会真正影响死锁的可能性。与排它锁相比,在存在可升级锁的情况下允许共享锁只是可升级锁的一个功能。


我们首先考虑一下可升级锁的用途。我们想象一下下面的情况:

  • 多个写入线程必须检查条件(读取操作),然后根据条件修改状态
  • 检查状况很昂贵
  • 该条件很少得到满足。
  • 其他线程也读取状态。

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

  1. Writer 获取独占锁,并开始检查条件
  2. 其他线程在运行昂贵的检查操作时必须阻塞 - 即使它们只需要读取。

可以认为检查-写入周期的读取部分甚至阻塞读取线程是一个缺点。那么,让我们考虑一个替代方案:

  1. Writer 获取共享锁,并开始检查条件
  2. 其他线程也可能获取共享锁
  3. Writer 检查了条件,并释放了读锁
  4. 条件已满足,编写器现在尝试获取独占锁以继续

在 3. 和 4. 之间,可能有多个写入者完成了状态检查(因为他们可以同时检查),并且现在正在竞相获取独占锁。只有一个人可以获胜,其他人必须阻止。当他们阻塞时,获胜者正在修改状态。

在这种情况下,等待获取排它锁的写入者不能假设他们检查的条件不再有效!另一个写入者可能已经在他们之前获取了锁,并且状态现在已被修改。他们可以忽略这一点,这可能会导致未定义的行为,具体取决于条件和修改的关系。或者,当他们获得排他锁时,他们可以再次检查条件,这将使我们恢复到第一种方法,除了由于竞争而无用的潜在冗余检查。不管怎样,这种方法比第一种方法更糟糕。


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

  1. Writer 获取特权锁,并开始检查条件
  2. 其他线程可能会获取共享锁
  3. Writer 检查了满足的条件,并升级为排它锁(必须阻塞,直到其他锁被释放)

让我们考虑一种情况,多个写入者被授予特权锁。他们可以同时检查昂贵的条件,这很好,但他们仍然有一场升级锁的竞赛。这次,竞争导致死锁,因为每个写入者都持有读锁,并等待所有读锁被释放才能升级。

如果可升级锁相对于其他可升级锁是排他的,则不会发生这种死锁,并且昂贵的检查和修改之间不存在竞争,但读取器线程在写入器检查时仍然可以操作。