Что такое дедлоки, когда они возникают и пример
Что проверяют
Понимаете ли вы причины взаимной блокировки в Go и знаете ли, что рантайм ловит далеко не все.
Как отвечать
Взаимная блокировка это ситуация, когда горутины ждут друг друга и ни одна не может продолжить.
Типичные причины в Go:
1. Канал без буфера и без второй стороны. Отправка блокируется, пока кто-то не начнёт читать:
ch := make(chan int)
ch <- 1 // fatal error: all goroutines are asleep - deadlock!2. Чтение из канала, который никто не закроет. range по каналу ждёт закрытия вечно.
3. Мьютекс захвачен дважды. sync.Mutex не реентрантный: повторный Lock в той же горутине блокирует её навсегда. Классика это метод, берущий блокировку и вызывающий другой метод того же объекта, который берёт её же.
4. Разный порядок захвата. Одна горутина берёт A потом B, другая B потом A. Обе ждут вторую блокировку.
5. Забытый Done у WaitGroup или Add после запуска горутины: Wait не вернётся.
Важная деталь: рантайм печатает all goroutines are asleep только когда заблокированы абсолютно все горутины. Если в приложении работает хотя бы одна, например обработчик HTTP, частичная взаимоблокировка не обнаруживается вовсе и выглядит как зависший запрос и растущее число горутин. Ищут это по pprof с профилем goroutine и по GODEBUG или детектору гонок.
Как не допускать: единый порядок захвата блокировок, defer mu.Unlock() сразу после Lock, не вызывать чужой код под блокировкой, context с ограничением времени на всех ожиданиях, select с case <-ctx.Done() или time.After вместо голого ожидания, буферизованный канал там, где отправитель не должен ждать.
Где спотыкаются
Считают мьютекс реентрантным.
Полагаются на то, что рантайм всегда обнаружит проблему.
Ставят defer wg.Done() не первой строкой горутины.
Держат блокировку во время сетевого вызова.