Ответ
В Go существует два основных подхода к взаимодействию между горутинами, которые отражают философию языка.
1. Каналы (Channels) — Идиоматичный способ
Это предпочтительный и идиоматичный способ в Go, который следует принципу:
"Do not communicate by sharing memory; instead, share memory by communicating."
("Не общайтесь, разделяя память; вместо этого, разделяйте память, общаясь.")
Каналы — это типизированные "трубы", через которые горутины могут безопасно отправлять и получать данные, обеспечивая синхронизацию без явных блокировок.
Пример:
// Создаем канал для передачи целых чисел
ch := make(chan int)
go func() {
// Отправляем данные в канал (операция блокируется, пока кто-то не прочитает)
ch <- 42
}()
// Читаем данные из канала (операция блокируется, пока кто-то не отправит)
value := <-ch
fmt.Println(value) // Выведет: 42
Ключевые особенности каналов:
- Буферизация: Каналы могут быть буферизированными (
make(chan int, 10)
), что позволяет отправлять несколько значений без немедленной блокировки. - Закрытие: Отправитель может закрыть канал (
close(ch)
), чтобы сигнализировать, что данные больше не будут отправляться. - Итерация: Можно итерироваться по каналу с помощью
for range
, цикл завершится после закрытия канала. - Мультиплексирование: Оператор
select
позволяет ожидать данные сразу из нескольких каналов, подобноswitch
.
2. Примитивы синхронизации из пакета sync
Этот подход более традиционен для других языков и используется, когда прямое управление состоянием с помощью блокировок более уместно.
sync.Mutex
иsync.RWMutex
: Используются для защиты доступа к общим данным.Mutex
предоставляет эксклюзивную блокировку, аRWMutex
позволяет множество читателей или одного писателя. Это полезно для защиты критических секций, например, при инкременте общего счетчика.sync.WaitGroup
: Позволяет горутине ожидать завершения работы группы других горутин.sync.Once
: Гарантирует, что определенный участок кода выполнится только один раз.- Пакет
sync/atomic
: Предоставляет низкоуровневые атомарные операции (например,atomic.AddInt64
), которые могут быть быстрее мьютексов для простых численных операций.