在RWMutex解锁后两次调用RWMutex RLock时的goroutine块

时间:2015-05-30 15:27:39

标签: concurrency go mutex

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()行,我刚刚解决了这样一个场景(花了我几个小时精确定位)。

1 个答案:

答案 0 :(得分:7)

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

sync's RWMutex.Lock的文档说:

  

为了确保锁定最终可用,阻止的锁定调用会阻止新读者获取锁定。

否则一系列读者在上一次发布之前都获得了读锁定,这可能会无限期地挨饿。

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

它不安全的原因是如果goroutine阻止获得第二次读锁(由于写入器被阻止),它将永远不会释放第一个读锁定。这将导致互斥锁上的每个未来锁定调用永远阻塞,解锁部分或全部程序。如果所有goroutine都被阻止,Go将只检测到死锁。