атомарные счетчики

This commit is contained in:
badkaktus 2019-10-08 16:42:41 +03:00
parent acb7585e84
commit f627d67e1a
3 changed files with 29 additions and 31 deletions

View File

@ -34,7 +34,7 @@ Select
Пулы воркеров (Worker Pools) Пулы воркеров (Worker Pools)
WaitGroups WaitGroups
Ограничение скорости (Rate Limiting) Ограничение скорости (Rate Limiting)
Atomic Counters Атомарные счетчики (Atomic Counters)
Mutexes Mutexes
Stateful Goroutines Stateful Goroutines
Sorting Sorting

View File

@ -1,9 +1,8 @@
// The primary mechanism for managing state in Go is // Основным механизмом управления состоянием в Go является
// communication over channels. We saw this for example // связь по каналам. Мы видели это, например, с [пулами воркеров](worker-pools).
// with [worker pools](worker-pools). There are a few other // Есть несколько других вариантов управления состоянием.
// options for managing state though. Here we'll // Здесь мы рассмотрим использование пакета `sync/atomic`
// look at using the `sync/atomic` package for _atomic // для _атомарных счетчиков_, к которым обращаются горутины.
// counters_ accessed by multiple goroutines.
package main package main
@ -15,38 +14,37 @@ import (
func main() { func main() {
// We'll use an unsigned integer to represent our // Мы будем использовать целое число без знака
// (always-positive) counter. // для представления нашего (всегда положительного)
// счетчика.
var ops uint64 var ops uint64
// A WaitGroup will help us wait for all goroutines // WaitGroup поможет нам подождать, пока все горутины
// to finish their work. // завершат свою работу.
var wg sync.WaitGroup var wg sync.WaitGroup
// We'll start 50 goroutines that each increment the // Мы запустим 50 горутин, каждая из которых увеличивает
// counter exactly 1000 times. // счетчик ровно в 1000 раз.
for i := 0; i < 50; i++ { for i := 0; i < 50; i++ {
wg.Add(1) wg.Add(1)
go func() { go func() {
for c := 0; c < 1000; c++ { for c := 0; c < 1000; c++ {
// To atomically increment the counter we // Для атомарного увеличения счетчика мы
// use `AddUint64`, giving it the memory // используем AddUint64, присваивая ему адрес
// address of our `ops` counter with the // памяти нашего счетчика `ops` с префиксом `&`.
// `&` syntax.
atomic.AddUint64(&ops, 1) atomic.AddUint64(&ops, 1)
} }
wg.Done() wg.Done()
}() }()
} }
// Wait until all the goroutines are done. // Ждем пока завершатся горутины.
wg.Wait() wg.Wait()
// It's safe to access `ops` now because we know // Теперь доступ к `ops` безопасен, потому что мы знаем,
// no other goroutine is writing to it. Reading // что никакие другие горутины не пишут в него. Безопасное
// atomics safely while they are being updated is // чтение атомарного счетчика во время его обновления также
// also possible, using functions like // возможно, используя функцию `atomic.LoadUint64`.
// `atomic.LoadUint64`.
fmt.Println("ops:", ops) fmt.Println("ops:", ops)
} }

View File

@ -1,11 +1,11 @@
# We expect to get exactly 50,000 operations. Had we # Мы ожидаем получить ровно 50 000 операций. Если бы
# used the non-atomic `ops++` to increment the counter, # мы использовали неатомарный `ops++` для увеличения
# we'd likely get a different number, changing between # счетчика, мы бы, вероятно, получили другое число,
# runs, because the goroutines would interfere with # изменяющееся между прогонами, потому что горутины
# each other. Moreover, we'd get data race failures # мешали бы друг другу. Более того, мы получим сбои
# when running with the `-race` flag. # в гонке данных при работе с флагом -race.
$ go run atomic-counters.go $ go run atomic-counters.go
ops: 50000 ops: 50000
# Next we'll look at mutexes, another tool for managing # Далее мы рассмотрим мьютексы, еще один способ
# state. # управления состоянием.