Причина тупика заключается в том, что ваш код будет дважды вызывать метод Lock()
одного и того же мьютекса, что является операцией блокировки.
Объяснение заключается в Спецификация: Селекторы:
Следующие правила применяются к селекторам:
- Для значения
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-я блокировка не будет блокироваться (ее мьютекс еще не заблокирован).