匿名互斥体和结构的死锁

Deadlock with anonymous mutex and struct

假设我有这两个结构:

type A struct {
    Mutex sync.Mutex
    i int
}

type B struct {
    A
    sync.Mutex
}

现在,当我尝试锁定 B 然后 A 我遇到了死锁:

var b B
b.Lock()
b.Mutex.Lock()
b.Mutex.Unlock()
b.Unlock()

我发现这和struct的mutex的名字有关A,比如我把它命名为Mutexx而不是Mutex就不会出现死锁.但我不知道为什么这很重要。谁能解释一下这种行为?

https://play.golang.org/p/UVi_WLWeGmi

死锁的原因是因为你的代码会两次调用同一个mutex的Lock()方法,这是一个阻塞操作。

解释在Spec: Selectors:

The following rules apply to selectors:

  1. For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.

这是什么意思?

B 中,您同时嵌入了 sync.MutexA 的值,并且 A 还嵌入了 sync.Mutex.

B.Mutex时,可以引用直接嵌入的B.Mutex字段(非限定类型名作为字段名),可以也指B.A.Mutex(因为A字段嵌入在B中),但是根据上面引用的规则,它会表示处的字段/方法最浅 深度,即B.Mutex.

同样,b.Lock() 可以引用 B.Mutex.Lock(),也可以引用 B.A.Mutex.Lock()。但同样根据引用的规则,它将表示 shallowest 深度的字段/方法,即 B.Mutex.Lock().

所以这段代码:

b.Lock()
b.Mutex.Lock()

会调用同一个MutexLock()方法两次,也就是B结构体的内嵌B.Mutex字段。第二次调用将阻塞,因为互斥量已被锁定。

当您将 A.Mutex 重命名为例如A.Mutexx,然后你写:

b.Lock()
b.Mutexx.Lock()

第一个 b.Lock() 调用引用 B.Mutex.Lock(),第二个 b.Mutexx.Lock() 调用引用 B.A.Mutexx.Lock() 调用,因此它们锁定了 2 个不同的互斥锁;它们是独立的,所以第二个锁不会阻塞(它的互斥量还没有被锁定)。