Что такое мьютекс и зачем он нужен
Что проверяют
Понимаете ли вы, что такое гонка данных и почему в Go она возникает, как только две горутины трогают одну переменную и хотя бы одна из них пишет. Следом спрашивают про sync.RWMutex, про deadlock или когда вместо мьютекса лучше канал или atomic.
Как отвечать
Мьютекс (mutual exclusion) гарантирует, что критическую секцию в каждый момент выполняет только одна горутина. Остальные на Lock() блокируются и ждут Unlock(). Без него небезопасен даже count++: это чтение, сложение и запись, и две горутины теряют инкременты друг друга.
Что стоит знать про sync.Mutex:
- нулевое значение готово к работе:
var mu sync.Mutexуже разблокированный мьютекс, конструктор не нужен; - идиома с
defer:mu.Lock()и сразуdefer mu.Unlock()снимают блокировку даже при панике и раннемreturn, накладные расходыdeferв современных версиях Go почти нулевые; - нереентерабельный: повторный
Lock()в той же горутине это вечная блокировка; - не копируется: мьютекс кладут в структуру рядом с данными, которые он охраняет, а методы объявляют на указателе. Копирование ловит
go vet; sync.RWMutex: много читателей черезRLock()или один писатель. Выигрыш есть, когда чтений заметно больше, чем записей;TryLock(): есть с Go 1.18, но нужен редко.
Для простого счётчика мьютекс избыточен: atomic.Int64 проще и быстрее. А если данные передаются между горутинами, а не делятся, естественнее канал. Завершите ответ практикой: гонки ищут флагом -race в go test и go run.
Где спотыкаются
Держат блокировку во время сетевого вызова или отправки в канал. Критическая секция должна быть короткой, иначе мьютекс превращает параллельный код в очередь.
Захватывают два мьютекса в разном порядке в разных местах. Это классический deadlock, лечится единым порядком захвата. Рантайм сам сообщает all goroutines are asleep только когда заблокированы вообще все горутины, в сервере вы просто получите зависший обработчик.
Защищают запись, но читают без блокировки, считая чтение безопасным. Для модели памяти Go это такая же гонка.