Какие паттерны синхронизации можно реализовать с помощью каналов?

Ответ

Да, каналы являются одним из основных и идиоматичных способов синхронизации горутин в Go. Вот несколько ключевых паттернов:

  1. Ожидание завершения (сигнал "готово") Это самый частый случай: главная горутина ждет, пока дочерняя горутина завершит свою работу. Для этого используется блокирующее чтение из канала.

    done := make(chan bool)
    
    go func() {
        // ... какая-то длительная работа ...
        fmt.Println("Горутина завершила работу")
        done <- true // Отправляем сигнал о завершении
    }()
    
    <-done // Блокируемся, пока не получим сигнал
    fmt.Println("Программа завершена")

    Для ожидания завершения группы горутин лучше подходит sync.WaitGroup.

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

    const MaxConcurrent = 3
    semaphore := make(chan struct{}, MaxConcurrent)
    
    for i := 0; i < 10; i++ {
        semaphore <- struct{}{} // Занимаем "слот"
        go func(id int) {
            defer func() { <-semaphore }() // Освобождаем "слот" после завершения
            // ... работа ...
            fmt.Printf("Задача %d выполненаn", id)
        }(i)
    }
  3. Мьютекс (Mutex) Канал с буфером размером 1 может работать как мьютекс для защиты критической секции.

    mutex := make(chan struct{}, 1)
    
    mutex <- struct{}{} // Блокировка
    // ... критическая секция ...
    <-mutex // Разблокировка

    Однако для этой цели почти всегда лучше, производительнее и понятнее использовать sync.Mutex.

  4. Оповещение нескольких горутин (Broadcast) Закрытие (close) канала немедленно разблокирует все горутины, которые ждут чтения из него. Это мощный механизм для одновременного оповещения множества подписчиков о событии.

    broadcast := make(chan struct{})
    
    for i := 0; i < 5; i++ {
        go func(id int) {
            <-broadcast // Все 5 горутин заблокируются здесь
            fmt.Printf("Горутина %d получила сигнал!n", id)
        }(i)
    }
    
    time.Sleep(1 * time.Second)
    close(broadcast) // Отправляем сигнал всем одновременно