В чем принципиальное различие между небуферизированным каналом и каналом с буфером размера 1?

Ответ

Нет, они не равнозначны, и различие между ними фундаментально. Оно заключается в синхронизации и блокировке операций отправки.

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

  • Полная синхронизация: Отправка (ch <- data) в такой канал блокирует горутину-отправителя до тех пор, пока другая горутина не будет готова принять данные (<-ch). И наоборот, чтение блокируется, пока кто-то не отправит данные.
  • Гарантия встречи: Это как передача эстафетной палочки. Отправитель не может продолжить свой путь, пока получатель не заберет палочку. Это гарантирует, что в момент разблокировки отправителя данные уже были получены.
  • Назначение: Используется для синхронизации горутин и гарантии того, что одно событие произошло перед другим.

Буферизированный канал с размером 1 (make(chan T, 1))

  • Асинхронность (ограниченная): Отправка в такой канал не блокируется, если буфер пуст. Горутина-отправитель может положить одно значение в канал и сразу продолжить выполнение, не дожидаясь получателя.
  • Блокировка при заполнении: Блокировка произойдет только при попытке отправить второе значение, пока первое еще не было прочитано из канала.
  • Назначение: Используется для развязки (decoupling) отправителя и получателя, чтобы они могли работать более независимо. Помогает сгладить кратковременные всплески нагрузки, когда отправитель производит данные чуть быстрее, чем получатель их обрабатывает.

Пример, демонстрирующий разницу

package main

import "fmt"

func main() {
    // Канал с буфером 1: отправка не блокирует, т.к. есть место в буфере
    bufferedChan := make(chan int, 1)
    bufferedChan <- 42 
    fmt.Println("Отправка в буферизированный канал прошла без блокировки")
    <-bufferedChan

    // Небуферизированный канал: эта программа вызовет deadlock
    // так как отправка блокируется, а в этой же горутине нет получателя.
    unbufferedChan := make(chan int)
    // unbufferedChan <- 42 // fatal error: all goroutines are asleep - deadlock!
    fmt.Println("Эта строка никогда не будет выполнена")
}

Краткий итог:

  • Небуферизированный канал — это точка рандеву для двух горутин.
  • Буферизированный канал размера 1 — это почтовый ящик, в который можно положить одно письмо, не дожидаясь почтальона.