Как избежать блокировки горутины при работе с небуферизированным каналом?

Ответ

Небуферизированный канал блокирует горутину-отправителя до тех пор, пока горутина-получатель не будет готова принять данные, и наоборот. Это синхронное поведение. Чтобы избежать блокировки, можно использовать два основных подхода:

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