← Назад к вопросам

Что такое атомарные операции? Для чего используется пакет sync/atomic?

2.2 Middle🔥 221 комментариев
#Конкурентность и горутины

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Атомарные операции в Go

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

Основные свойства атомарных операций:

  • Неделимость — операция не может быть прервана другими горутинами
  • Видимость — изменения становятся сразу видны всем горутинам
  • Порядок — обеспечивают частичное упорядочивание памяти (memory ordering)

Пакет sync/atomic в Go

Пакет sync/atomic предоставляет низкоуровневые атомарные операции для синхронизации доступа к памяти в конкурентных программах. Он используется для реализации примитивов синхронизации и безопасного доступа к общим данным без использования мьютексов.

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

  1. Производительность — атомарные операции обычно быстрее мьютексов для простых операций
  2. Избежание блокировок — позволяют реализовывать lock-free алгоритмы
  3. Синхронизация без мьютексов — для простых счётчиков, флагов и указателей
  4. Реализация примитивов синхронизации — мьютексы, семафоры и другие структуры часто используют atomic внутри

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

package main

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

func main() {
    // Атомарный счётчик
    var counter int32 = 0
    var wg sync.WaitGroup
    
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            atomic.AddInt32(&counter, 1) // Атомарное увеличение
            wg.Done()
        }()
    }
    
    wg.Wait()
    fmt.Println("Counter:", atomic.LoadInt32(&counter)) // Атомарное чтение
    
    // Compare-and-Swap (CAS) операция
    var value int32 = 10
    swapped := atomic.CompareAndSwapInt32(&value, 10, 20)
    fmt.Println("Swapped:", swapped, "Value:", value)
    
    // Атомарное хранение и загрузка указателей
    var data *string
    str := "hello"
    atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&data)), unsafe.Pointer(&str))
    
    loaded := (*string)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&data))))
    fmt.Println("Loaded:", *loaded)
}

Ключевые функции пакета atomic:

Для целочисленных типов (int32, int64, uint32, uint64, uintptr):

  • Add — атомарное сложение
  • Load — атомарное чтение
  • Store — атомарная запись
  • Swap — атомарная замена с возвратом старого значения
  • CompareAndSwap (CAS) — сравнение и замена (основа многих lock-free алгоритмов)

Для указателей:

  • LoadPointer, StorePointer, SwapPointer, CompareAndSwapPointer

Специальные типы:

  • Value — тип для атомарного хранения любых значений (с Go 1.17)
var config atomic.Value
config.Store(map[string]string{"mode": "production"})
currentConfig := config.Load().(map[string]string)

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

1. Lock-free счётчик:

type Counter struct {
    value int64
}

func (c *Counter) Increment() {
    atomic.AddInt64(&c.value, 1)
}

func (c *Counter) Value() int64 {
    return atomic.LoadInt64(&c.value)
}

2. Атомарный флаг:

type Service struct {
    running int32 // 0 = stopped, 1 = running
}

func (s *Service) Start() {
    if atomic.CompareAndSwapInt32(&s.running, 0, 1) {
        // Запуск сервиса
    }
}

3. Double-checked locking (шаблон):

type Singleton struct {
    data string
}

var (
    instance *Singleton
    initialized uint32
    mu sync.Mutex
)

func GetInstance() *Singleton {
    if atomic.LoadUint32(&initialized) == 1 {
        return instance
    }
    
    mu.Lock()
    defer mu.Unlock()
    
    if initialized == 0 {
        instance = &Singleton{data: "config"}
        atomic.StoreUint32(&initialized, 1)
    }
    
    return instance
}

Когда использовать atomic вместо mutex:

Используйте atomic когда:

  • Операции простые (инкремент, декремент, замена значения)
  • Нужна максимальная производительность
  • Реализуете lock-free структуры данных
  • Работаете с флагами или счётчиками

Используйте mutex когда:

  • Операции сложные (много строк кода)
  • Нужно выполнить несколько операций как одну атомарную
  • Работаете со сложными структурами данных

Важные предостережения:

  1. Порядок операций — атомарные операции гарантируют только атомарность, но не полное упорядочивание операций
  2. ABA проблема — в CAS операциях значение может измениться и вернуться обратно
  3. Типовая безопасность — требуется осторожность с приведением типов
  4. Сложность отладки — lock-free код сложнее отлаживать

Пакет sync/atomic — это мощный инструмент для опытных разработчиков, который позволяет создавать высокопроизводительные конкурентные структуры данных, но требует глубокого понимания модели памяти Go и принципов многопоточного программирования.

Что такое атомарные операции? Для чего используется пакет sync/atomic? | PrepBro