Какие существуют способы синхронизации и координации горутин в Go?

Ответ

В Go есть несколько основных механизмов для синхронизации и координации работы горутин. Главный принцип Go: "Не обменивайтесь данными, разделяя память; разделяйте память, обмениваясь данными".

  1. Каналы (Channels) — основной и идиоматичный способ для коммуникации и передачи данных между горутинами. Гарантируют безопасную передачу.

    ch := make(chan string)
    go func() { 
        ch <- "данные" // Отправка данных в канал
    }()
    msg := <-ch // Блокирующее чтение из канала
  2. Мьютексы (sync.Mutex) — используются для защиты критических секций кода и обеспечения эксклюзивного доступа к общим данным.

    var mu sync.Mutex
    var counter int
    
    mu.Lock()   // Блокируем доступ
    counter++
    mu.Unlock() // Освобождаем
  3. sync.RWMutex — более производительный мьютекс для ситуаций, когда чтений гораздо больше, чем записей. Позволяет множеству читателей работать одновременно, но запись требует эксклюзивной блокировки.

    var rw sync.RWMutex
    rw.RLock()   // Блокировка на чтение
    rw.RUnlock() // Снятие блокировки на чтение
    rw.Lock()    // Блокировка на запись
  4. sync.WaitGroup — позволяет дождаться завершения работы группы горутин.

    var wg sync.WaitGroup
    for i := 0; i < 5; i++ {
        wg.Add(1) // Увеличиваем счетчик горутин
        go func() {
            defer wg.Done() // Уменьшаем счетчик по завершении
            // ... работа ...
        }()
    }
    wg.Wait() // Ожидаем, пока счетчик не станет равен нулю
  5. Атомарные операции (sync/atomic) — для простых операций (инкремент, сложение, CAS), которые должны выполняться без прерываний. Работают быстрее мьютексов, но применимы только к примитивным типам.

    var count int64
    atomic.AddInt64(&count, 1)
  6. sync.Once — гарантирует, что определенный участок кода выполнится только один раз за все время работы программы, например, для ленивой инициализации синглтонов.

    var once sync.Once
    once.Do(func() { 
        // Этот код выполнится только один раз
    })
  7. Контекст (context) — используется для передачи сигналов отмены (cancellation), таймаутов и request-scoped данных через вызовы API и между горутинами. Это ключевой механизм для управления жизненным циклом горутин.