在 RWMutex Unlock 之后两次调用 RWMutex RLock 时 goroutine 阻塞

goroutine blocks when calling RWMutex RLock twice after an RWMutex Unlock

var mu sync.RWMutex

go func() {
    mu.RLock()
    defer mu.RUnlock()

    mu.RLock()  // In my real scenario this second lock happened in a nested function.
    defer mu.RUnlock()

    // More code.
}()

mu.Lock()
mu.Unlock()  // The goroutine above still hangs.

如果一个函数读锁定一个 read/write 互斥锁两次,而另一个函数先写锁,然后写 un 锁定同一个互斥锁,原始函数仍然挂起。

这是为什么?是不是因为有互斥体允许代码执行的串行顺序?

我刚刚通过删除第二行 mu.RLock() 解决了这样的情况(我花了几个小时才查明)。

这是读写锁的几种标准行为之一。什么Wikipedia calls "Write-preferring RW locks".

sync's RWMutex.Lock 的文档说:

To ensure that the lock eventually becomes available, a blocked Lock call excludes new readers from acquiring the lock.

否则,在前一个释放之前获得读锁的一系列读取器可能会无限期地饿死写入。

这意味着在同一个 goroutine 已经读取锁定的 RWMutex 上调用 RLock 总是不安全的。 (顺便说一句,Lock 在常规互斥量上也是如此,因为 Go 的互斥量不支持递归锁定。)

它不安全的原因是,如果 goroutine 曾经阻止获取第二个读锁(由于写入器被阻止),它永远不会释放第一个读锁。这将导致以后对互斥量的每次锁定调用都将永远阻塞,从而使部分或全部程序陷入死锁。如果所有 goroutine 都被阻塞,Go 只会检测死锁。