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.
如果函数读取/写入互斥锁两次,而另一个函数写入锁定然后写入 un 锁定相同的互斥锁,则原始函数仍会挂起。
为什么?是因为互斥量允许代码执行的序列顺序是什么?
通过删除第二条mu.RLock()
行,我刚刚解决了这样一个场景(花了我几个小时精确定位)。
答案 0 :(得分:7)
这是读写锁的几种标准行为之一。 Wikipedia calls "Write-preferring RW locks"。
sync's
RWMutex.Lock
的文档说:
为了确保锁定最终可用,阻止的锁定调用会阻止新读者获取锁定。
否则一系列读者在上一次发布之前都获得了读锁定,这可能会无限期地挨饿。
这意味着在RLock
上调用同一goroutine已读锁定的RWMutex
总是不安全的。 (顺便说一句,对于常规互斥锁,Lock
也是如此,因为Go的互斥锁不支持递归锁定。)
它不安全的原因是如果goroutine阻止获得第二次读锁(由于写入器被阻止),它将永远不会释放第一个读锁定。这将导致互斥锁上的每个未来锁定调用永远阻塞,解锁部分或全部程序。如果所有goroutine都被阻止,Go将只检测到死锁。