Что произойдет, если в канал Go отправляются данные, но их никто не читает?

Ответ

Последствия зависят от типа канала: небуферизованный или буферизованный.

1. Небуферизованный канал (make(chan int))

Отправка в такой канал является синхронной операцией. Это означает, что горутина-отправитель будет немедленно заблокирована до тех пор, пока другая горутина не будет готова прочитать данные из этого канала.

  • Последствие: Если никто никогда не прочитает данные, горутина-отправитель останется заблокированной навсегда. Это называется утечкой горутины (goroutine leak) и может привести к deadlock, если основная программа ждет завершения этой горутины.
// Эта горутина заблокируется навсегда, так как никто не читает из ch.
func main() {
    ch := make(chan int)
    go func() {
        ch <- 42 // Блокировка здесь
    }()
    // Программа "зависнет" или завершится с deadlock, если все горутины спят
    select{}
}

2. Буферизованный канал (make(chan int, N))

Отправка в такой канал является асинхронной, пока в буфере есть свободное место.


  • Горутина-отправитель может успешно отправить N значений в канал без блокировки.



  • При попытке отправить N+1-ое значение, когда буфер полон, горутина-отправитель будет заблокирована — точно так же, как и в случае с небуферизованным каналом.



  • Последствие: Если буфер заполнится и никто не будет читать из канала, горутина-отправитель также будет заблокирована навсегда, что приведет к утечке горутины и возможному deadlock.


ch := make(chan int, 1) // Буфер размером 1
ch <- 1 // OK, не блокирует
ch <- 2 // Блокировка! Буфер полон, и никто не читает.

Как избежать проблем:

  • Управляйте жизненным циклом: Убедитесь, что для каждой горутины-отправителя есть горутина-получатель, или что у горутины есть способ завершиться, даже если отправка блокируется (например, через context или select с таймаутом).
  • Закрывайте каналы: Когда отправитель закончил посылать данные, он должен закрыть канал с помощью close(ch). Это сигнализирует получателям, что данных больше не будет.
  • Используйте select с default: Для неблокирующей отправки можно использовать конструкцию select:
select {
case ch <- data:
    // Данные успешно отправлены
default:
    // Канал заблокирован, выполняем альтернативное действие
}