Ответ
Пакет sync/atomic
в Go предоставляет низкоуровневые атомарные примитивы, которые позволяют выполнять операции с памятью (чтение, запись, сложение) таким образом, чтобы они не были прерваны другими горутинами. Это гарантирует, что операция будет выполнена целиком, без риска возникновения состояния гонки (race condition
).
Атомарные операции реализованы с использованием специальных инструкций процессора и работают значительно быстрее, чем мьютексы, но их область применения ограничена.
Пример: безопасный счетчик
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++ {
// Безопасно увеличиваем счетчик на 1
atomic.AddInt64(&counter, 1)
}
}()
}
wg.Wait()
// Читаем итоговое значение атомарно
finalValue := atomic.LoadInt64(&counter)
fmt.Println("Final Counter:", finalValue) // Всегда будет 100000
}
Когда использовать атомарные операции (вместо мьютексов):
- Для простых операций со счетчиками или флагами: Когда нужно просто увеличить число, установить флаг (
true
/false
) или обновить один указатель. Для таких задач мьютекс является избыточным. - Для оптимизации производительности в критических участках кода: Когда блокировка мьютекса создает заметное узкое место, атомарные операции могут дать существенный прирост скорости.
- При реализации lock-free алгоритмов: Для создания сложных конкурентных структур данных, которые работают без использования блокировок.
Основные функции:
AddInt64
,AddUint64
, и т.д. — атомарное сложение.LoadInt64
,LoadPointer
— атомарное чтение значения.StoreInt64
,StorePointer
— атомарная запись значения.SwapInt64
— атомарно записывает новое значение и возвращает старое.CompareAndSwapInt64
(CAS) — атомарно сравнивает значение с ожидаемым и, если они равны, записывает новое. Это основа для многих lock-free алгоритмов.