Что такое mutex и семафоры
Что проверяют
Понимаете ли вы разницу между взаимным исключением и ограничением параллелизма, и знаете ли, чем мьютекс в Go отличается от мьютекса из учебника по системному программированию.
Как отвечать
Мьютекс защищает общее состояние: в критическую секцию входит одна горутина, остальные ждут. В Go это sync.Mutex с методами Lock и Unlock, а для данных, которые читают часто и пишут редко, есть sync.RWMutex. Нулевое значение уже готово к работе, конструктор не нужен.
Семафор ограничивает число одновременных операций: не «один», а «не больше N». Классическая реализация это буферизованный канал, куда кладут токен перед работой и забирают после. Есть и пакет golang.org/x/sync/semaphore со взвешенными разрешениями, и errgroup с методом SetLimit.
sem := make(chan struct{}, 8)
for _, u := range urls {
sem <- struct{}{}
go func(u string) {
defer func() { <-sem }()
fetch(u)
}(u)
}Сильный ответ добавит идиому языка: мьютекс для разделяемого состояния, каналы для передачи владения данными. И назовёт инструмент: гонки ищут детектором go test -race, а не вычитыванием кода глазами.
Где спотыкаются
Говорят, что мьютекс в Go принадлежит захватившей горутине. Не принадлежит: Unlock вправе сделать любая горутина. При этом повторный Lock в той же горутине даёт дедлок, потому что мьютекс не рекурсивный, и рекурсивной версии в стандартной библиотеке нет намеренно.
Копируют структуру с мьютексом внутри. Метод с получателем по значению или передача структуры в функцию создают отдельную копию блокировки, и защита исчезает молча; go vet такое ловит.
Забывают defer Unlock и теряют разблокировку на ветке с ошибкой или на панике. Обратная крайность тоже встречается: defer внутри цикла держит блокировку до конца функции, а не до конца итерации.