В чем разница между небуферизированным каналом (`make(chan T)`) и буферизированным каналом с размером буфера 1 (`make(chan T, 1)`)?

Ответ

Основное различие заключается в их поведении при отправке данных и, как следствие, в способе синхронизации горутин.

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

  • Синхронный: Обеспечивает прямое "рандеву" между отправителем и получателем.
  • Блокировка при отправке: Операция отправки (ch <- data) блокирует горутину-отправителя до тех пор, пока другая горутина не будет готова принять данные из этого канала (<-ch).
  • Блокировка при получении: Операция получения (<-ch) блокирует горутину-получателя до тех пор, пока другая горутина не отправит данные в этот канал.
  • Аналогия: Похоже на передачу эстафетной палочки — бегун не может отпустить палочку, пока следующий бегун её не возьмёт.

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

  • Асинхронный: Отправитель и получатель могут работать независимо, пока буфер не заполнится или не опустеет.
  • Блокировка при отправке: Отправка (ch <- data) блокируется, только если буфер уже заполнен. Если буфер пуст, отправитель помещает в него значение и немедленно продолжает выполнение, не дожидаясь получателя.
  • Блокировка при получении: Как и у небуферизированного, блокируется, если канал (его буфер) пуст.

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

package main

import "fmt"

func main() {
    // Буферизированный канал размером 1
    bufferedCh := make(chan int, 1)
    bufferedCh <- 1 // Не блокируется, т.к. в буфере есть место
    fmt.Println("Отправка в буферизированный канал не заблокировала выполнение")
    // следующая отправка заблокирует, пока кто-то не прочитает
    // bufferedCh <- 2 

    // Небуферизированный канал
    unbufferedCh := make(chan int)
    fmt.Println("Сейчас произойдет блокировка при отправке в небуферизированный канал...")
    // Эта строка вызовет deadlock, если закомментировать горутину ниже,
    // так как никто не готов принять данные.
    go func() { 
        data := <-unbufferedCh
        fmt.Printf("Данные [%d] получены из небуферизированного каналаn", data)
    }()
    unbufferedCh <- 100 // Блокируется, пока горутина выше не будет готова принять
    fmt.Println("Отправка в небуферизированный канал успешно завершена")
}

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