Ответ
Блокировка (deadlock) в Go — это ситуация, когда одна или несколько горутин бесконечно ожидают события, которое никогда не произойдет. Чаще всего это связано с неправильной работой с каналами.
Основные причины блокировок с каналами:
-
Отправка в небуферизованный канал без получателя. Небуферизованный канал (
make(chan T)
) требует, чтобы отправитель и получатель встретились одновременно. Если горутина пытается отправить данные, а получателя нет, она блокируется навсегда.func main() { ch := make(chan int) ch <- 1 // Deadlock! Нет другой горутины, готовой принять значение. }
-
Отправка в заполненный буферизованный канал. Буферизованный канал (
make(chan T, N)
) блокирует отправку только тогда, когда его буфер полон, и нет готового получателя.func main() { ch := make(chan int, 1) // Буфер размером 1 ch <- 1 // OK, значение помещено в буфер ch <- 2 // Deadlock! Буфер полон, и никто не читает из канала. }
-
Чтение из пустого канала, который никто не закроет. Горутина, читающая из канала, будет заблокирована, пока в него не придут данные или пока канал не будет закрыт.
Способы избежать блокировки:
- Запускать отправителей и получателей в разных горутинах. Это основной паттерн конкурентной работы в Go.
- Использовать
select
сdefault
для неблокирующей отправки/чтения:select { case ch <- value: // Успешно отправлено default: // Канал заблокирован, выполняем альтернативное действие }
- Использовать
select
с таймаутом, чтобы избежать вечного ожидания:select { case val := <-ch: // Получили значение case <-time.After(1 * time.Second): // Не дождались за 1 секунду }
- Правильно закрывать каналы. Канал закрывается отправителем, чтобы сообщить получателям, что данных больше не будет.