Что происходит, если создать канал в Go без указания размера буфера, и в чем его ключевое отличие от буферизованного?

Ответ

Если при создании канала с помощью make(chan T) не указать размер, создается небуферизованный (синхронный) канал. Его ключевое отличие — отсутствие внутреннего буфера для хранения значений.

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


  1. Блокировка при отправке: Операция отправки в канал (ch <- value) заблокирует горутину-отправителя до тех пор, пока другая горутина не будет готова принять значение из этого канала.



  2. Блокировка при чтении: Операция чтения из канала (value := <-ch) заблокирует горутину-получателя до тех пор, пока другая горутина не отправит значение в этот канал.



  3. Синхронизация (Рандеву): Отправка и получение данных происходят одновременно. Это делает небуферизованные каналы мощным инструментом для синхронизации горутин, гарантируя, что одна горутина не продолжит выполнение, пока другая не достигнет определенной точки.


Пример:

package main

import (
    "fmt"
    "time"
)

func main() {
    ch := make(chan string) // Небуферизованный канал

    go func() {
        fmt.Println("Горутина: готовлюсь отправить данные...")
        ch <- "Привет!" // Эта строка заблокируется, пока main не будет готова читать
        fmt.Println("Горутина: данные успешно отправлены.")
    }()

    fmt.Println("Main: жду 2 секунды перед чтением...")
    time.Sleep(2 * time.Second)

    fmt.Println("Main: готовлюсь прочитать данные...")
    message := <-ch // Разблокирует горутину-отправителя
    fmt.Printf("Main: получил сообщение: '%s'n", message)
}

Отличие от буферизованного канала

Буферизованный канал (make(chan T, N), где N > 0) имеет внутренний буфер размером N.

  • Отправка в него не блокируется, пока в буфере есть свободное место.
  • Чтение из него не блокируется, пока в буфере есть хотя бы один элемент.

Блокировка происходит только при попытке записи в переполненный буфер или чтения из пустого.