Какие методы у мьютекса для блокировки на чтение и запись в Go
Что проверяют
Знаете ли вы не только названия методов, но и семантику sync.RWMutex: кто кого ждёт и почему он не всегда быстрее обычного sync.Mutex. Следом обычно просят объяснить, что будет при повторном RLock, или предложить альтернативу мьютексу.
Как отвечать
Методы стоит называть вместе с поведением:
sync.Mutex:Lock()иUnlock(), с Go 1.18 ещёTryLock(). Нулевое значение готово к работе;RLock()иRUnlock(): разделяемая блокировка на чтение, её одновременно держат сколько угодно горутин;Lock()иUnlock()уRWMutex: эксклюзивная блокировка на запись, ждёт ухода всех читателей;TryLock()иTryRLock(): попытка без ожидания, тоже с Go 1.18. Документация прямо предупреждает, что правильные сценарии для них редки;RLocker(): возвращаетsync.Locker, у которогоLockиUnlockвызываютRLockиRUnlock.
Ключевое свойство: если писатель уже ждёт в Lock(), новые RLock() тоже встают в очередь. Так писатель не голодает, но отсюда и дедлок при повторном захвате на чтение в одной горутине.
Когда брать RWMutex: чтений заметно больше, чем записей, и секция чтения не крошечная. На коротких секциях он часто медленнее Mutex, потому что делает больше атомарных операций. Альтернативы: atomic.Pointer[T] с подменой снимка целиком, sync.Map для кешей с редкой записью, каналы, если задача про передачу владения данными.
Привычки, которые стоит упомянуть: defer mu.Unlock() сразу после захвата, мьютекс рядом с данными, которые он охраняет, прогон go test -race.
Где спотыкаются
Пытаются повысить блокировку: держат RLock и зовут Lock в той же горутине. Писатель ждёт ухода читателя, читатель ждёт писателя, программа висит. Такого апгрейда в Go нет.
Копируют структуру с мьютексом, например через метод с получателем по значению. У копии свой мьютекс, защита исчезает. go vet ловит это проверкой copylocks.
Пишут в карту под RLock. Несколько горутин пишут одновременно, и программа рискует упасть с fatal error: concurrent map writes, которую не перехватить через recover.