В Golang дедлок чаще всего возникает при работе с мьютексами и каналами.
Возможные случаи:
Циклическая зависимость мьютексов: Две или более горутины пытаются получить блокировку на мьютексы в разном порядке, создавая взаимоблокировку.
go
go
Блокирующая отправка в небуферизованный канал, который никто не читает: Горутина отправляет данные в канал, но нет другой горутины, готовой их принять.
go
Блокирующее чтение из канала, в который никто не пишет: Горутина пытается прочитать данные из канала, но никто не отправляет в него данные.
go
Использование одного и того же мьютекса в рекурсивной функции без RWMutex:
go
Отправка в закрытый канал или чтение из закрытого канала без проверки: Отправка в закрытый канал вызовет панику, чтение из закрытого канала вернет нулевое значение и false, но если не проверять второй возвращаемый параметр, можно некорректно обработать ситуацию и потенциально привести к дедлоку в другом месте.
Неправильное использование sync.WaitGroup: Например, вызов Done() раньше, чем было выполнено необходимое количество Add(), или вызов Wait() раньше, чем все горутины завершили свою работу.
Таблица с примерами:
| Ситуация | Механизм | Пример |
|---|---|---|
| Циклическая зависимость мьютексов | sync.Mutex | Горутина A ждет B, Горутина B ждет A |
| Блокирующая отправка в канал | Небуферизованный канал | ch <- data без получателя |
| Блокирующее чтение из канала | Канал | <-ch без отправителя |
| Рекурсивное использование мьютекса | sync.Mutex | Повторная блокировка в той же горутине |
| Работа с закрытым каналом без проверки | Канал | Отправка в закрытый канал |
Неправильное использование WaitGroup | sync.WaitGroup | Ошибки в логике Add/Done/Wait |