Атомарные счетчики (Atomic Counters)|Atomic Counters
#
// Основным механизмом управления состоянием в Go является
// связь по каналам. Мы видели это, например, с [пулами воркеров](worker-pools).
// Есть несколько других вариантов управления состоянием.
// Здесь мы рассмотрим использование пакета `sync/atomic`
// для _атомарных счетчиков_, к которым обращаются горутины.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// Мы будем использовать целое число без знака
// для представления нашего (всегда положительного)
// счетчика.
var ops uint64
// WaitGroup поможет нам подождать, пока все горутины
// завершат свою работу.
var wg sync.WaitGroup
// Мы запустим 50 горутин, каждая из которых увеличивает
// счетчик ровно в 1000 раз.
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
for c := 0; c < 1000; c++ {
// Для атомарного увеличения счетчика мы
// используем AddUint64, присваивая ему адрес
// памяти нашего счетчика `ops` с префиксом `&`.
atomic.AddUint64(&ops, 1)
}
wg.Done()
}()
}
// Ждем пока завершатся горутины.
wg.Wait()
// Теперь доступ к `ops` безопасен, потому что мы знаем,
// что никакие другие горутины не пишут в него. Безопасное
// чтение атомарного счетчика во время его обновления также
// возможно, используя функцию `atomic.LoadUint64`.
fmt.Println("ops:", ops)
}
# Мы ожидаем получить ровно 50 000 операций. Если бы
# мы использовали неатомарный `ops++` для увеличения
# счетчика, мы бы, вероятно, получили другое число,
# изменяющееся между прогонами, потому что горутины
# мешали бы друг другу. Более того, мы получим сбои
# в гонке данных при работе с флагом -race.
$ go run atomic-counters.go
ops: 50000
# Далее мы рассмотрим мьютексы, еще один способ
# управления состоянием.