Что такое atomic-операции в Go и для чего нужен пакет `sync/atomic`?

Ответ

Атомарные (atomic) операции в Go — это операции, которые выполняются как единое, неделимое действие, без риска быть прерванными другим потоком (горутиной). Это гарантирует потокобезопасность при работе с разделяемыми данными без необходимости использования более медленных примитивов синхронизации, таких как мьютексы.

Пакет sync/atomic предоставляет низкоуровневые атомарные примитивы, которые напрямую транслируются в инструкции процессора.

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

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

Основные операции в sync/atomic:

  • Add: Атомарно добавляет значение (например, AddInt64).
  • Load: Атомарно читает значение.
  • Store: Атомарно записывает значение.
  • Swap: Атомарно записывает новое значение и возвращает старое.
  • CompareAndSwap (CAS): Атомарно сравнивает значение с ожидаемым и, если они совпадают, заменяет его новым. Это одна из самых мощных атомарных операций.

Пример использования:

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

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

    wg.Add(100)
    for i := 0; i < 100; i++ {
        go func() {
            defer wg.Done()
            // Безопасный инкремент счетчика из разных горутин
            atomic.AddInt64(&counter, 1)
        }()
    }

    wg.Wait()

    // Для чтения также используем атомарную операцию
    finalValue := atomic.LoadInt64(&counter)
    fmt.Println("Final Counter:", finalValue) // => Final Counter: 100
}

Когда использовать:

Атомарные операции идеально подходят для реализации счетчиков, флагов состояний или для управления простыми разделяемыми переменными, где производительность критична.