Как определить дедлок в Go и когда он возникает?
Что проверяют
Знаете ли вы, какие дедлоки рантайм Go ловит сам, а какие молча превращаются в утечку горутин. Следом обычно просят найти зависшую горутину в работающем сервисе.
Как отвечать
Дедлок это ситуация, когда горутины ждут друг друга и ни одна не может продолжить. Типичные причины:
- небуферизованный канал: отправка
ch <- v, которую никто не принимает, или чтение, в которое никто не пишет; - незакрытый канал:
for v := range chвисит вечно, если отправитель забылclose(ch); - порядок блокировок: одна горутина берёт
mu1, потомmu2, другая наоборот; - повторный захват:
sync.Mutexне реентерабелен, и метод под блокировкой, вызвавший другой метод с тем же мьютексом, повиснет. Уsync.RWMutexопасен иRLockвнутриRLock, если между ними встал ожидающий писатель; sync.WaitGroup: забытыйDone()на ветке с раннимreturnоставляетWaitвисеть навсегда.
Рантайм падает с fatal error: all goroutines are asleep, только если заблокированы все горутины программы. В сервере с листенером и таймерами этого не бывает: зависшие горутины просто висят, а память растёт. Поэтому смотрят так:
net/http/pprof:/debug/pprof/goroutine?debug=2отдаёт стеки всех горутин с состоянием (chan send,chan receive, ожидание мьютекса) и временем ожидания в минутах;- SIGQUIT:
kill -QUIT <pid>печатает стеки всех горутин и завершает процесс; - тесты:
go test -timeoutпри зависании падает со стеками, аgo.uber.org/goleakпроверяет, что после теста не осталось лишних горутин.
Предотвращение: единый порядок захвата мьютексов, defer mu.Unlock(), никакого чужого кода под блокировкой, select с ctx.Done() в операциях с каналами.
Где спотыкаются
Надеются на go vet или -race. go vet дедлоки не ищет, а детектор гонок находит гонки данных, а не взаимные блокировки.
Приводят пример, где main делает time.Sleep и выходит. Программа завершается без ошибки, и дедлок никто не замечает: это как раз показательный случай.
Лечат зависание буферизованным каналом. Буфер откладывает блокировку до заполнения и прячет ошибку в логике, а не исправляет её.