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

Что будет с горутиной, если попытаться записать значение в заполненный буфер?

2.0 Middle🔥 171 комментариев
#Основы Go

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

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

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

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

Когда горутина пытается записать значение в буферизованный канал (chan), буфер которого полностью заполнен, её поведение зависит от контекста операции — блокируется до тех пор, пока в буфере не освободится место. Это одно из фундаментальных свойств каналов в Go, обеспечивающее синхронизацию и безопасную коммуникацию между горутинами.

Механизм блокировки

  • Буферизованный канал имеет очередь фиксированной ёмкости, задаваемой при создании. Например, ch := make(chan int, 3) создаёт канал с буфером на 3 элемента.
  • Пока в буфере есть свободные слоты, операция записи (ch <- value) выполняется немедленно и без блокировки.
  • Если буфер полностью заполнен, горутина, выполняющая запись, приостанавливает выполнение и помещается в очередь ожидающих писателей.

Пример с блокировкой

package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int, 2) // Буфер на 2 элемента
	
	// Заполняем буфер
	ch <- 1
	ch <- 2
	
	fmt.Println("Буфер заполнен. Пытаемся записать третье значение...")
	
	go func() {
		ch <- 3 // Эта горутина заблокируется
		fmt.Println("Запись 3 выполнена (после освобождения буфера)")
	}()
	
	time.Sleep(1 * time.Second) // Даём время для запуска горутины
	
	fmt.Println("Читаем из канала, освобождая место...")
	<-ch // Читаем первое значение, освобождая слот в буфере
	
	time.Sleep(1 * time.Second) // Ждём, чтобы горутина-писатель успела выполниться
}

В этом примере:

  1. Буфер заполняется значениями 1 и 2.
  2. Анонимная горутина пытается записать 3, но блокируется.
  3. После чтения значения из канала буфер освобождается, и заблокированная горутина возобновляет выполнение.

Важные аспекты поведения

  • Deadlock (взаимная блокировка): Если все горутины заблокированы и ни одна не может освободить буфер, runtime Go определяет deadlock и аварийно завершает программу.
  • Небуферизованные каналы: Для них запись блокируется всегда, пока другая горутина не готова читать, так как буфер отсутствует.
  • Использование select с default: Можно избежать блокировки с помощью конструкции select.

Пример с неблокирующей записью

select {
case ch <- value:
    // Запись успешна
default:
    // Буфер заполнен - выполняется альтернативная логика
    fmt.Println("Не удалось записать - буфер полон")
}

В этом случае горутина не блокируется, а сразу переходит к ветке default, если запись невозможна.

Практические следствия

  1. Синхронизация: Блокировка при записи используется для координации между производителями и потребителями данных.
  2. Ограничение ресурсов: Буферизованные каналы часто применяют для ограничения количества одновременных операций (pattern "semaphore channel").
  3. Предотвращение потери данных: Блокировка гарантирует, что данные не будут "потеряны" из-за переполнения.

Внутренняя реализация

При блокировке горутина помещается в очередь ожидания канала, связанную с операцией записи. Планировщик Go (scheduler) снимает её с выполнения. Когда другая горутина выполняет чтение из канала:

  1. Освобождается слот в буфере.
  2. Планировщик активирует одну из заблокированных горутин-писателей.
  3. Активированная горутина помещает значение в буфер и продолжает выполнение.

Таким образом, попытка записи в заполненный буферизованный канал приводит к предсказуемой и управляемой блокировке горутины — это не ошибка, а важный механизм синхронизации, который делает конкурентные программы в Go безопасными и эффективными.

Что будет с горутиной, если попытаться записать значение в заполненный буфер? | PrepBro