← Назад к вопросам

В какой момент произойдет блокировка канала с размерностью 100

2.0 Middle🔥 242 комментариев
#Конкурентность и горутины

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Механизм блокировки буферизованных каналов в Go

В Go канал с размерностью 100 является буферизованным каналом (buffered channel). Его поведение отличается от небуферизованного, и блокировка происходит не моментально, а при определенных условиях. Вот детальный разбор.

Когда происходит блокировка?

Блокировка операций с буферизованным каналом зависит от двух основных операций: отправки (ch <- value) и приема (<- ch).

1. Блокировка при отправке (send)

Отправка в канал блокируется только когда буфер заполнен. Для канала с размерностью 100 это означает:

  • Первые 100 отправок (ch <- value) выполнятся немедленно без блокировки, так как значения помещаются в буфер.
  • 101-я отправка заблокирует горутину, пока в буфере не освободится место (т.е. пока другая горутина не прочитает хотя бы одно значение).
ch := make(chan int, 100) // Буфер на 100 элементов

for i := 0; i < 100; i++ {
    ch <- i // Не блокируется, буфер заполняется
}

ch <- 101 // БЛОКИРУЕТСЯ здесь! Буфер полон.

2. Блокировка при приеме (receive)

Прием из канала блокируется только когда буфер пуст. Для канала с размерностью 100:

  • Если в буфере есть данные, прием (<-ch) выполняется немедленно, извлекая значение из буфера.
  • Если буфер пуст, операция приема заблокирует горутину, пока другая горутина не отправит данные.
ch := make(chan int, 100)

// Горутина-отправитель заполняет буфер
go func() {
    for i := 0; i < 100; i++ {
        ch <- i
    }
}()

// Горутина-получатель читает 101 раз
for i := 0; i < 101; i++ {
    <-ch // 101-е чтение БЛОКИРУЕТСЯ, если больше нет отправителей!
}

Ключевые отличия от небуферизованных каналов

  • Небуферизованный канал (make(chan int)) блокирует и отправителя, и получателя сразу, пока обе стороны не будут готовы (rendezvous).
  • Буферизованный канал действует как очередь FIFO:
    • Отправитель блокируется только при переполнении.
    • Получатель блокируется только при опустошении.

Практический пример с deadlock

Рассмотрим классическую ошибку:

func main() {
    ch := make(chan int, 100)
    
    // Заполняем буфер полностью
    for i := 0; i < 100; i++ {
        ch <- i
    }
    
    // Попытка отправить еще раз в той же горутине
    ch <- 101 // БЛОКИРУЕТСЯ навсегда → deadlock!
    // Нет других горутин, которые могли бы прочитать из канала
}

Важные нюансы

  1. Deadlock detection — runtime Go обнаружит deadlock, если все горутины заблокированы.
  2. Закрытие канала — после close(ch) отправка вызовет panic, но прием будет работать, пока буфер не опустеет.
  3. Select с default — позволяет избежать блокировки:
select {
case ch <- value:
    // Отправка успешна
default:
    // Буфер полон, не блокируемся
}

Вывод

Для канала с размерностью 100 блокировка отправки наступит при попытке отправить 101-й элемент без освобождения буфера, а блокировка приема — при попытке прочитать из пустого буфера без активных отправителей. Понимание этого механизма критически важно для написания корректных конкурентных программ на Go, избегания deadlock и проектирования эффективных систем обмена данными между горутинами.