Atomic Counters

Атомарні Лічильники|Atomic Counters #

// Як ми вже дізнались, основним механізмом роботи управлінням стану
// в Go є, канали комунікації (або комунікація каналами).
// Ми побачили це на прикладі "[пулу працівників](worker-pools)" на додадчу
// до інших випадків.
// Але існують і інші способи управління станом та асинхронністю,
// розглянемо, наприклад, пакет `sync/atomic` для
// _атомарних лічильників_, доступ до яких є у кількох горутин.

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

func main() {

	// Ось ми використаємо беззнакове ціле для представлення
	// нашого (завжди додатнього) лічильника.
	var ops atomic.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++ {
				// To atomically increment the counter we use `Add`.
				ops.Add(1)
			}

			wg.Done()
		}()
	}

	// Чекаємо поки усі горутини не відпрацюють.
	wg.Wait()

	// Жодна горутина не записує до 'ops', але використаємо
	// `Load` для безпечного читання значення (навіть тоді коли інши
	// горутини його оновлюють).

	fmt.Println("ops:", ops.Load())
}
# Ми очікувано отримали рівно 50000 операцій.
# Як би ми використали не атомарний 'ops++'
# числа б - були інші. Ми доречі можемо
# отримувати дані про стан гонки даних
# коли запускаємо з прапорцем  `-race`.
$ go run atomic-counters.go
ops: 50000

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