Ответ
Небуферизированный канал (unbuffered channel) — это канал с нулевой емкостью, создаваемый с помощью make(chan T)
.
Ключевая особенность — синхронность. Операции отправки и получения данных блокируются до тех пор, пока на другом конце канала не появится готовая к обмену горутина. Этот момент синхронизации называется рандеву (rendezvous).
- Отправка (
ch <- data
) блокируется, пока другая горутина не будет готова прочитать данные из этого же канала (<-ch
). - Получение (
<-ch
) блокируется, пока другая горутина не отправит данные в этот канал (ch <- data
).
Пример, демонстрирующий блокировку:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int) // Небуферизированный канал
go func() {
fmt.Println("Горутина: начинаю отправку данных...")
ch <- 42 // Блокируется здесь, пока main() не будет готов принять
fmt.Println("Горутина: данные успешно отправлены.")
}()
time.Sleep(2 * time.Second) // Даем время горутине запуститься и заблокироваться
fmt.Println("Main: готовлюсь принять данные...")
val := <-ch // Разблокирует горутину, приняв данные
fmt.Printf("Main: данные приняты: %dn", val)
}
Когда следует использовать:
- Для синхронизации горутин: Когда нужно гарантировать, что одно событие произошло перед другим. Например, дождаться завершения работы воркера.
- Для гарантированной доставки: Отправитель точно знает, что его сообщение было получено, так как операция отправки завершится только после получения.
- Для предотвращения состояния гонки при передаче владения: Безопасная передача указателя или другого ресурса от одной горутины к другой.
Важно: Неправильное использование небуферизированных каналов — частая причина взаимных блокировок (deadlock). Например, если горутина пытается отправить данные в канал, из которого никто никогда не прочитает.