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)
}

Программа показывает, что было выполнено примерно 40 000 операций.

$ go run atomic-counters.go
ops: 40200

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

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