首页 > 解决方案 > 连续两次检测同一个goroutine调用sync.Mutex.Lock()

问题描述

此代码(单线程程序)将永远无法工作:

func TestDoubleLockPanics(t *testing.T) {
    var mu sync.Mutex
    mu.Lock()
    mu.Lock()
}

然而,当我运行这个测试时,并没有恐慌。竞争检测器不会打印出数据竞争。go vet没有抱怨,没有日志消息,它只是永远阻塞。

(我关心的实际代码显然不是这么简单——我只是把它归结为本质。)

当持有锁的线程试图重新获取同一个锁时,有什么方法可以让 Go 大声告诉我?

标签: golockingmutexreentrancy

解决方案


Go 的sync.Mutex对象不会跟踪哪个 goroutine 锁定了它们。或者,换句话说,没有锁定线程:只有一个数据锁。

此外,正如上面的评论Unlock所说,您可以从其他一些 goroutine 中解锁它们,而不是锁定它们的那个。Lock因此,调用两次实际上是有意义的。这是一个骨架示例:

func f() {
    mu.Lock()
    ... if this is not just an example, some code goes here ...
    // now wait for g() to call mu.Unlock()
    mu.Lock()
    fmt.Println("we got here, so g() must have done an unlock")
    mu.Unlock()
}

func g() {
    ... some code probably goes here too ...
    mu.Unlock() // allow f() to proceed
    ... more code, perhaps ...
}

在这种情况下,我们不希望在f第二次Lock调用中出现恐慌。

您可以构建自己的递归锁或其他技术,但由于每个 goroutine 的内部 ID 是隐藏的,因此非常棘手在这个答案的评论中有一个实现的链接Recursive locking in Go,但我从来没有使用过它,甚至没有仔细看过它。


推荐阅读