Go в примерах: Атомарные счётчики (Atomic Counters)

Основной механизм управления состоянием в Go – связь через каналы. Вы видели это в примере с набором обработчиков. Но есть несколько других вариантов для управления состоянием. В этом примере мы рассмотрим использование пакета sync/atomic для атомарных счетчиков, доступных нескольким горутинам.

package main
import "fmt"
import "time"
import "sync/atomic"
import "runtime"
func main() {

Мы будем использовать беззнаковое целое число для представления нашего (всегда положительного) счетчика.

    var ops uint64 = 0

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

    for i := 0; i < 50; i++ {
        go func() {
            for {

Для атомарного увеличения счётчика используем функцию AddUint64, передавая ей указатель на адрес в памяти, где хранится ops, с использованием в синтаксисе &.

                atomic.AddUint64(&ops, 1)

Разрешим остальным горутинам продолжить работу.

                runtime.Gosched()
            }
        }()
    }

Подождем секунду для накопления результатов работы.

    time.Sleep(time.Second)

Чтобы безопасно использовать счётчик в то время, пока он обновляется другими горутинами, сделаем копию текущего значения в opsFinal через LoadUint64. Как уже делалось выше, нужно передать этой функции адрес памяти &ops, из которого будет получено значение.

    opsFinal := atomic.LoadUint64(&ops)
    fmt.Println("ops:", opsFinal)
}

Запущенная программа показывает 40000 выполненных операций.

$ go run atomic-counters.go
ops: 40200

Далее мы обратим наше внимание на мьютексы и иные инструменты для управления состоянием.

Следующий пример: Мьютексы (Mutexes).