Тупик с анонимным мьютексом и структурой - PullRequest
0 голосов
/ 26 ноября 2018

Допустим, у меня есть эти две структуры:

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()

Iвыяснил, что это связано с именем мьютекса структуры A, например, нет тупика, если я назову его Mutexx, а не Mutex.Но я не знаю, почему это важно.Кто-нибудь может, пожалуйста, объяснить это поведение?

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

1 Ответ

0 голосов
/ 26 ноября 2018

Причина тупика заключается в том, что ваш код будет дважды вызывать метод Lock() одного и того же мьютекса, что является операцией блокировки.

Объяснение заключается в Спецификация: Селекторы:

Следующие правила применяются к селекторам:

  1. Для значения x типа T или *T, где T не является указателем илиТип интерфейса, x.f обозначает поле или метод на наименьшей глубине в T, где есть такой f.Если не совсем один f с наименьшей глубиной, выражение селектора недопустимо.

Что это значит?

In B, вы встраиваете sync.Mutex и значение A, а A также встраивает sync.Mutex.

Когда вы пишете B.Mutex, это может относиться к непосредственно внедренному *Поле 1038 * (неквалифицированное имя типа действует как имя поля), и может также ссылаться на B.A.Mutex (потому что поле A встроено в B), но согласно указанному правилувыше, оно будет обозначать поле / метод на глубине 1045 *, которая равна B.Mutex.

Аналогично, b.Lock() может относиться к B.Mutex.Lock() и может относиться к B.A.Mutex.Lock().Но, опять же, согласно указанному правилу, оно будет обозначать поле / метод на глубине 1053 *, которая равна B.Mutex.Lock().

. Итак, этот код:

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

Вызовет метод Lock() одного и того же Mutex дважды, который является встроенным полем B.Mutex структуры B.Второй вызов заблокируется, так как мьютекс уже заблокирован.

Когда вы переименуете A.Mutex, например, A.Mutexx, а затем напишите:

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

Первый b.Lock()вызов относится к B.Mutex.Lock(), а второй b.Mutexx.Lock() вызов относится к B.A.Mutexx.Lock() вызову, поэтому они блокируют 2 разных, различных мьютекса;они независимы, поэтому 2-я блокировка не будет блокироваться (ее мьютекс еще не заблокирован).

...