Что произойдет при попытке параллельной записи в небуферизированный канал в Go?

Ответ

Попытка параллельной записи в небуферизированный канал приведет к deadlock (взаимной блокировке).

Объяснение:

Небуферизированный канал (make(chan T)) требует, чтобы и отправитель, и получатель были готовы к обмену данными одновременно (это называется рандеву). Операция записи в такой канал блокирует горутину-отправителя до тех пор, пока другая горутина не будет готова прочитать из этого канала.

Если несколько горутин попытаются одновременно записать данные в один и тот же небуферизированный канал, только одна из них сможет завершить операцию (и то, только если есть готовый читатель). Все остальные горутины-писатели заблокируются. Если количество читателей меньше количества писателей, заблокированные горутины никогда не смогут продолжить выполнение, что и вызовет deadlock.

Пример:

package main

import (
    "fmt"
    "time"
)

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

    go func() {
        fmt.Println("Горутина 1: пытается отправить 1")
        ch <- 1 // Блокируется здесь
        fmt.Println("Горутина 1: отправила 1")
    }()

    go func() {
        fmt.Println("Горутина 2: пытается отправить 2")
        ch <- 2 // И эта горутина тоже блокируется
        fmt.Println("Горутина 2: отправила 2")
    }()

    // Так как нет читателей, обе горутины заблокируются навсегда.
    // Среда выполнения Go обнаружит это и завершит программу с ошибкой:
    // fatal error: all goroutines are asleep - deadlock!
    time.Sleep(time.Second)
}

Решения:

  1. Использовать буферизированный канал (make(chan int, N)), если вы хотите разрешить отправку N значений без немедленной блокировки.
  2. Организовать пул воркеров, где чтение и запись синхронизированы, например, с помощью sync.WaitGroup.
  3. Использовать select с default для неблокирующей отправки.