Ответ
Небуферизированный канал блокирует горутину-отправителя до тех пор, пока горутина-получатель не будет готова принять данные, и наоборот. Это синхронное поведение. Чтобы избежать блокировки, можно использовать два основных подхода:
1. Неблокирующие операции с select
и default
Оператор select
позволяет горутине ожидать нескольких операций одновременно. Если добавить ветку default
, select
перестает быть блокирующим: если ни одна из операций в case
не готова, выполняется default
.
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int) // Небуферизированный канал
// Попытка неблокирующей отправки
select {
case ch <- 42:
fmt.Println("Значение было успешно отправлено")
default:
// Этот блок выполнится, так как нет получателя, готового принять данные
fmt.Println("Отправка не удалась: получатель не готов")
}
// Запускаем получателя в отдельной горутине
go func() {
time.Sleep(1 * time.Second) // Имитируем работу
v := <-ch
fmt.Printf("Горутина получила значение: %dn", v)
}()
// Попытка неблокирующего чтения
select {
case v := <-ch:
fmt.Printf("Основная горутина получила значение: %dn", v)
default:
// Этот блок выполнится, так как отправитель еще не готов
fmt.Println("Чтение не удалось: в канале нет данных")
}
time.Sleep(2 * time.Second) // Даем время горутине завершиться
}
2. Использование буферизированных каналов
Буферизированный канал (make(chan int, N)
) имеет внутренний буфер размером N
. Отправка в такой канал блокируется только тогда, когда буфер полон. Чтение блокируется, только когда буфер пуст. Это позволяет отправителю и получателю работать более асинхронно.
// Канал с буфером на 1 элемент
bufCh := make(chan int, 1)
// Эта отправка не заблокируется, так как в буфере есть место
bufCh <- 100
fmt.Println("Успешно отправлено в буферизированный канал")
// Попытка второй отправки заблокирует горутину, пока кто-то не прочитает из канала
// bufCh <- 200