Windows 上 `std::shared_mutex::unlock_shared()` 的死锁问题解析:原因和解决方案
2024-03-07 11:15:19
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 的更新和公告。