// Основным механизмом управления состоянием в 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) }