Прежде всего источник golang всегда меняется, поэтому давайте убедимся, что мы смотрим на одно и то же. Принять релиз 1.12 на
https://github.com/golang/go/blob/release-branch.go1.12/src/sync/mutex.go
как вы сказали, начинается функция блокировки
func (m *Mutex) Lock() {
// fast path where it will set the high order bit and return if not locked
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
return
}
//reads value to decide on the lower order bits
for {
//if statements involving CompareAndSwaps on the lower order bits
}
}
Что делает этот CompareAndSwap? он выглядит атомарно в этом int32 и, если он равен 0, он меняет его на mutexLocked (который равен 1, как указано выше как const) и возвращает true, что он поменял его местами.
Тогда это быстро возвращается. Это его быстрый путь. Программа заполучила блокировку, и теперь она может запустить защищенный путь.
Если оно уже равно 1 (mutexLocked), оно не меняет его и возвращает false (оно не менялось).
Затем он читает состояние и входит в цикл, который выполняет атомарное сравнение, и меняет местами, чтобы определить, как он должен себя вести.
Каковы возможные состояния? комбинации заблокированных, проснувшихся и голодных, как вы видите из константного блока.
Теперь, в зависимости от того, сколько времени программа ожидала в списке ожидания, она получит приоритет, когда снова проверить, свободен ли мьютекс.
Но также обратите внимание, что только Unlock () может установить бит mutexLocked обратно в 0.
в цикле Lock () CAS единственными установленными битами являются голодные и пробужденные. Да, у вас может быть несколько читателей, но только один писатель в любое время, и этот писатель является тем, кто держит мьютекс и выполняет его защищенный путь до вызова Unlock (). Проверьте эту статью для более подробной информации.