Что такое пакет `sync/atomic` в Go и в каких случаях его стоит использовать?

Ответ

Пакет sync/atomic предоставляет низкоуровневые атомарные операции (такие как сложение, загрузка, сохранение, сравнение и замена), которые выполняются как единая, неделимая инструкция процессора. Это позволяет безопасно изменять разделяемые данные из нескольких горутин без использования блокировок (mutex).

Ключевые преимущества:

  • Производительность: Атомарные операции значительно быстрее мьютексов, так как они не вызывают блокировку потока ОС и переключение контекста.
  • Lock-free алгоритмы: Позволяют создавать неблокирующие алгоритмы для высококонкурентных сценариев.

Основные сценарии использования:

  1. Счётчики производительности: Безопасное увеличение/уменьшение счётчиков.
  2. Флаги состояния: Атомарное изменение флагов (например, isDone).
  3. Безопасная однократная инициализация: С помощью atomic.StorePointer и atomic.LoadPointer.

Пример: Атомарный счётчик

import (
    "fmt"
    "sync"
    "sync/atomic"
)

func main() {
    var counter int64
    var wg sync.WaitGroup

    // Запускаем 1000 горутин, каждая увеличивает счётчик 100 раз
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for j := 0; j < 100; j++ {
                atomic.AddInt64(&counter, 1)
            }
        }()
    }

    wg.Wait()
    fmt.Println("Итоговое значение счётчика:", counter) // Ожидаемый результат: 100000
}

Atomic vs. Mutex

  • atomic: Используйте для простых операций над примитивными типами (int32, int64, uintptr, unsafe.Pointer). Идеально для счётчиков, флагов.
  • sync.Mutex: Используйте для защиты сложных структур или для выполнения нескольких операций как единой транзакции. Мьютекс блокирует доступ ко всему защищаемому участку кода для других горутин.