Go за Прикладом: Атомарні Лічильники

Work in Progress / Сайт в процесі розробки

Основним механізмом управління станом в Go є, комунікація за допомогою каналів. Ми вже бачили це на прикладі “пул працівників”. Але існують і інші способи управління станом, наприклад зараз розглянемо пакет sync/atomic для атомарних лічильників, доступ до яких є у кількох горутин.

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

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

    var ops uint64

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

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

Для автоматичного інкременту ми користуємось методом AddUint64, якому даємо вказівник на комірку памяті де наш ops розташовується.

                atomic.AddUint64(&ops, 1)

Мілісекундна пауза в роботі.

                time.Sleep(time.Millisecond)
            }
        }()
    }

Чекаємо близько 1єї секунди, надаючи ops акумулювати деяке значення.

    time.Sleep(time.Second)

Для того, щоб безпечно використовувати лічильник який все ще оновлюється іншими горутинами, ми отримуємо копію значення за допомогою LoadUint64 і передаємо її opsFinal Так само як і в попередньому прикладі інкрементації, нам потрібно надати вказівник на комірку памяті, щоб функція LoadUint64 могла зняти копію цього значення.

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

Запуск программи покаже що ми виконали близько 40,000 операцій.

$ go run atomic-counters.go
ops: 41419

Надалі ми однайомимось з mutexeами, ще одниз з інструментів Go призначеним для управління станом.

Наступний приклад: Mutexes.