Дедлок (тупик) — это ситуация, когда две или более горутины блокируются, ожидая друг друга, и ни одна из них не может продолжить выполнение. Чаще всего возникает при работе с мьютексами или каналами.
Примеры дедлока:
Блокировка мьютексов в разном порядке:
go
Goroutine1 захватила mu1 и ждет mu2. Goroutine2 захватила mu2 и ждет mu1. Обе горутины заблокированы.
Отправка в небуферизованный канал без получателя:
go
Отправка в небуферизованный канал блокируется до тех пор, пока другая горутина не начнет читать из него. Если получателя нет, происходит дедлок.
Чтение из канала без отправителя (после закрытия или пустой буферизованный):
go
Чтение из закрытого непустого канала завершится успешно. Чтение из закрытого и пустого канала или из открытого, но пустого небуферизованного канала без отправителя приведет к блокировке.
Golang-рантайм может обнаружить определенные типы дедлоков (например, когда все заблокированные горутины ожидают получения или отправки единственной горутины), и в таком случае программа аварийно завершится с сообщением "fatal error: all goroutines are asleep - deadlock!". Однако, не все дедлоки могут быть автоматически обнаружены.
Для предотвращения дедлоков важно:
select с default веткой или тайм-аутами при необходимости.