Ответ
В Go есть несколько основных механизмов для синхронизации и координации работы горутин. Главный принцип Go: "Не обменивайтесь данными, разделяя память; разделяйте память, обмениваясь данными".
-
Каналы (Channels) — основной и идиоматичный способ для коммуникации и передачи данных между горутинами. Гарантируют безопасную передачу.
ch := make(chan string) go func() { ch <- "данные" // Отправка данных в канал }() msg := <-ch // Блокирующее чтение из канала
-
Мьютексы (
sync.Mutex
) — используются для защиты критических секций кода и обеспечения эксклюзивного доступа к общим данным.var mu sync.Mutex var counter int mu.Lock() // Блокируем доступ counter++ mu.Unlock() // Освобождаем
-
sync.RWMutex
— более производительный мьютекс для ситуаций, когда чтений гораздо больше, чем записей. Позволяет множеству читателей работать одновременно, но запись требует эксклюзивной блокировки.var rw sync.RWMutex rw.RLock() // Блокировка на чтение rw.RUnlock() // Снятие блокировки на чтение rw.Lock() // Блокировка на запись
-
sync.WaitGroup
— позволяет дождаться завершения работы группы горутин.var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) // Увеличиваем счетчик горутин go func() { defer wg.Done() // Уменьшаем счетчик по завершении // ... работа ... }() } wg.Wait() // Ожидаем, пока счетчик не станет равен нулю
-
Атомарные операции (
sync/atomic
) — для простых операций (инкремент, сложение, CAS), которые должны выполняться без прерываний. Работают быстрее мьютексов, но применимы только к примитивным типам.var count int64 atomic.AddInt64(&count, 1)
-
sync.Once
— гарантирует, что определенный участок кода выполнится только один раз за все время работы программы, например, для ленивой инициализации синглтонов.var once sync.Once once.Do(func() { // Этот код выполнится только один раз })
-
Контекст (
context
) — используется для передачи сигналов отмены (cancellation), таймаутов и request-scoped данных через вызовы API и между горутинами. Это ключевой механизм для управления жизненным циклом горутин.