返回

Windows 上 `std::shared_mutex::unlock_shared()` 的死锁问题解析:原因和解决方案

windows

Windows 上 std::shared_mutex::unlock_shared() 的诡异阻塞问题

前言

在 Windows 操作系统上使用 C++11 引入的 std::shared_mutex 时,我们遭遇了一系列棘手的死锁问题。我们经过深入调查,发现了 std::shared_mutex 的 Windows 实现中的一个潜在缺陷,导致在没有活跃排他锁的情况下,unlock_shared() 会莫名其妙地阻塞。

问题

在特定情况下,当一个子线程成功获取共享锁时,其他所有子线程都会卡在 lock_shared() 中,导致整个程序陷入死锁。在 Windows 上,lock_shared() 会调用 RtlAcquireSRWLockShared,而 unlock_shared() 会调用 RtlReleaseSRWLockShared,它们都是 SRW 锁原语的 Windows 实现。

调查过程

我们排除了代码中的用户错误和竞争条件等可能的原因。我们使用各种工具和技术,包括调试器和性能分析器,来深入研究死锁。我们发现,死锁总是在 unlock_shared() 调用中发生,即使当时没有活跃的排他锁。

可能的解释

我们怀疑是 SRW 锁的 Windows 实现中存在缺陷。在 Windows 中,SRW 锁是由称为“事件”的内核对象实现的。我们推测,在某些情况下,即使没有排他锁,事件也可以进入“无法重置”状态,从而导致 unlock_shared() 阻塞。

潜在解决方案

由于我们无法直接修改 SRW 锁的 Windows 实现,因此我们探索了其他解决方法:

  • 报告给 Microsoft: 我们向 Microsoft 报告了此问题,希望他们能调查并提供修复。
  • 使用 std::latch C++20 中引入的 std::latch 可以用作共享锁的替代方案,因为它不依赖于 SRW 锁。
  • 避免在没有排他锁的情况下调用 unlock_shared() 虽然这不是一个理想的解决方案,但它可以防止死锁。

结论

std::shared_mutex 在 Windows 上的实现存在一个潜在缺陷,导致 unlock_shared() 即使没有活跃的排他锁也会阻塞。我们提供了可能的解决方法,并鼓励读者在遇到类似问题时探索这些选项。

常见问题解答

Q1:这个问题在其他平台上是否存在?

A1:这个问题仅在 Windows 操作系统上发现,在非 Windows 平台上未被观察到。

Q2:如何判断我是否遇到了此问题?

A2:死锁的症状是 lock_shared()unlock_shared() 调用中出现意外的阻塞。

Q3:我应该使用哪种解决方法?

A3:最佳的解决方法取决于具体情况。如果您无法避免在没有排他锁的情况下调用 unlock_shared(),那么报告给 Microsoft 或使用 std::latch 是更可靠的选择。

Q4:是否有任何已知的替代方案?

A4:除了 std::latch 之外,还有其他并发原语可以使用,例如读写锁和互斥锁。

Q5:Microsoft 何时会发布此缺陷的修复程序?

A5:修复程序的发布日期尚不清楚。我们鼓励您定期检查 Microsoft 的更新和公告。