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.
如果一个函数对一个读写互斥锁进行了两次读锁,而另一个函数对同一个互斥锁进行了写锁和write-un锁,那么原函数仍然挂起。
为什么?这是因为互斥体允许代码执行的串行顺序吗?
我刚刚通过删除第二个mu.RLock()
行解决了这样的场景(花了我几个小时来确定)。
这是读写锁的几种标准行为之一。维基百科称之为"写优先RW锁"。
sync's
RWMutex.Lock
的文档说:
为了确保锁最终可用,阻塞的lock调用会排除新的读取器获取锁。
否则,在前一个读锁释放之前获得读锁的一系列读锁可能会无限期地饿死写。
这意味着在一个已经读锁定的RWMutex
上调用RLock
总是不安全的。(顺便说一下,Lock
在常规互斥锁上也是如此,因为Go的互斥锁不支持递归锁定。)
它不安全的原因是,如果线程程序阻塞了第二个读锁(由于阻塞的写器),它将永远不会释放第一个读锁。这将导致以后对互斥锁的每次锁调用永远阻塞,导致部分或全部程序死锁。Go只会在所有例程都被阻塞的情况下检测死锁。