Какие примитивы синхронизации из пакета `sync`, помимо каналов, вы использовали?

Ответ

Хотя каналы являются предпочтительным способом организации взаимодействия между горутинами, пакет sync предоставляет незаменимые инструменты для традиционной синхронизации на основе разделяемой памяти.

Я использовал следующие примитивы:

  1. sync.Mutex: Мьютекс для защиты критических секций. Он гарантирует, что только одна горутина может выполнять определенный участок кода в любой момент времени. Это необходимо для предотвращения состояния гонки при доступе к общим данным.

  2. sync.RWMutex: Блокировка чтения-записи. Более производительный аналог Mutex, когда есть много читателей и редкие операции записи. Он позволяет неограниченному числу горутин одновременно читать данные, но запись блокирует всех (и читателей, и писателей).

  3. sync.WaitGroup: Позволяет дождаться завершения работы группы горутин. Основные методы:

    • Add(n): Увеличивает счетчик горутин на n.
    • Done(): Уменьшает счетчик (обычно вызывается в defer внутри горутины).
    • Wait(): Блокирует выполнение, пока счетчик не станет равен нулю.
    package main
    
    import (
        "fmt"
        "sync"
        "time"
    )
    
    func worker(id int, wg *sync.WaitGroup) {
        // Уменьшаем счетчик WaitGroup, когда горутина завершается.
        defer wg.Done()
        fmt.Printf("Worker %d startingn", id)
        time.Sleep(time.Second)
        fmt.Printf("Worker %d donen", id)
    }
    
    func main() {
        var wg sync.WaitGroup
    
        for i := 1; i <= 3; i++ {
            // Увеличиваем счетчик перед запуском горутины.
            wg.Add(1)
            go worker(i, &wg)
        }
    
        // Ждем, пока все горутины вызовут Done().
        wg.Wait()
        fmt.Println("All workers completed")
    }
  4. sync.Once: Гарантирует, что определенный участок кода будет выполнен ровно один раз за все время работы приложения. Идеально подходит для инициализации синглтонов или глобальных конфигураций.

  5. sync.Pool: Кэш для временных объектов, который помогает снизить нагрузку на сборщик мусора. Часто используется для переиспользования буферов, объектов запросов и т.д.

Выбор между каналами и примитивами sync зависит от задачи: каналы — для передачи владения данными и координации, а мьютексы — для защиты состояния.