Ответ
Буферизированный канал в Go — это канал, имеющий внутренний буфер для временного хранения данных. Это позволяет отправителю и получателю работать асинхронно, пока буфер не заполнится или не опустеет.
Создание
- Буферизированный:
ch := make(chan int, 3)
— создается канал с буфером на 3 элемента. - Небуферизированный:
ch := make(chan int)
— буфер отсутствует (его емкость равна 0).
Ключевые отличия в поведении
Характеристика | Небуферизированный канал | Буферизированный канал |
---|---|---|
Отправка (ch <- val ) | Блокируется, пока другая горутина не будет готова принять данные. | Не блокируется, если в буфере есть место. Блокируется, только если буфер полон. |
Прием (<- ch ) | Блокируется, пока другая горутина не отправит данные. | Не блокируется, если в буфере есть данные. Блокируется, только если буфер пуст. |
Синхронизация | Прямая синхронизация (рандеву). Отправитель и получатель должны "встретиться". | Асинхронная работа. Отправитель и получатель развязаны во времени. |
Внутреннее устройство (упрощенно)
Буферизированный канал можно представить как потокобезопасную кольцевую очередь (ring buffer) фиксированного размера, защищенную мьютексом.
- При отправке элемент добавляется в "хвост" очереди.
- При получении элемент забирается из "головы" очереди.
- Мьютекс обеспечивает безопасный доступ из нескольких горутин.
Пример
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 2) // Буфер на 2 элемента
ch <- 1 // Не блокируется
ch <- 2 // Не блокируется, буфер заполнен
fmt.Println("Буфер полон. Следующая отправка заблокируется.")
go func() {
time.Sleep(1 * time.Second)
fmt.Println("Принято:", <-ch) // Освобождаем место в буфере
}()
ch <- 3 // Эта операция была заблокирована, пока получатель не забрал '1'
fmt.Println("Отправка '3' завершена")
fmt.Println("Принято:", <-ch)
fmt.Println("Принято:", <-ch)
}
Основные сценарии использования
- Сглаживание нагрузки: Когда отправитель генерирует данные всплесками, а получатель обрабатывает их равномерно.
- Ограничение параллелизма: Можно использовать для создания пула воркеров, где размер буфера ограничивает количество одновременно выполняемых задач.