【问题标题】:Deadlock with anonymous mutex and struct匿名互斥锁和结构的死锁
【发布时间】:2018-11-26 14:32:23
【问题描述】:

假设我有这两个结构:

type A struct {
    Mutex sync.Mutex
    i int
}

type B struct {
    A
    sync.Mutex
}

现在,当我尝试锁定 BA 时,我遇到了死锁:

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

我发现这与结构A的互斥锁的名称有关,例如,如果我将其命名为Mutexx而不是Mutex,则不会出现死锁。但我不知道为什么这很重要。谁能解释一下这种行为?

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

【问题讨论】:

标签: go struct mutex deadlock embedding


【解决方案1】:

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

解释在Spec: Selectors:

以下规则适用于选择器:

  1. 对于T*T 类型的值x,其中T 不是指针或接口类型,x.f 表示最浅 深度的字段或方法T 哪里有这样的f。如果没有最浅深度的one f,则选择器表达式是非法的。

这是什么意思?

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()。但是再次根据引用的规则,它将表示最浅深度的字段/方法,即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 个不同的、不同的互斥锁;它们是独立的,因此第二个锁不会阻塞(它的互斥锁尚未锁定)。

【讨论】:

  • 好的,谢谢。事实上,我只是不知道可以通过匿名字段的类型名称来访问它。规范中描述这一点的部分就足够了。
  • @vonaka 即在Spec: Struct types: "用类型声明但没有显式字段名称的字段称为嵌入字段。嵌入字段必须指定为类型名称T或者作为指向非接口类型名*T的指针,而T本身可能不是指针类型。非限定类型名充当字段名。"
猜你喜欢
  • 2015-10-26
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 2011-03-03
  • 2010-12-17
  • 1970-01-01
相关资源
最近更新 更多