Ответ
Да, каналы являются одним из основных и идиоматичных способов синхронизации горутин в Go. Вот несколько ключевых паттернов:
-
Ожидание завершения (сигнал "готово") Это самый частый случай: главная горутина ждет, пока дочерняя горутина завершит свою работу. Для этого используется блокирующее чтение из канала.
done := make(chan bool) go func() { // ... какая-то длительная работа ... fmt.Println("Горутина завершила работу") done <- true // Отправляем сигнал о завершении }() <-done // Блокируемся, пока не получим сигнал fmt.Println("Программа завершена")
Для ожидания завершения группы горутин лучше подходит
sync.WaitGroup
. -
Семафор Буферизованный канал можно использовать для ограничения количества одновременно работающих горутин.
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) }
-
Мьютекс (Mutex) Канал с буфером размером 1 может работать как мьютекс для защиты критической секции.
mutex := make(chan struct{}, 1) mutex <- struct{}{} // Блокировка // ... критическая секция ... <-mutex // Разблокировка
Однако для этой цели почти всегда лучше, производительнее и понятнее использовать
sync.Mutex
. -
Оповещение нескольких горутин (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) // Отправляем сигнал всем одновременно