Атомарный счетчик в Go
Для простого счетчика можно обойтись без
замка: процессор выполняет приращение
одной неделимой операцией. Пакет
sync/atomic дает типы с методами,
которые безопасны при вызове из разных
горутин одновременно.
Тип atomic.Int64 хранит число внутри.
Метод Add прибавляет заданную
величину и возвращает новое значение;
Load читает текущее число без
гонки. Для целого счетчика этого часто
достаточно вместо Mutex.
Сто веток с атомарным приращением:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var n atomic.Int64
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
defer wg.Done()
n.Add(1)
}()
}
wg.Wait()
fmt.Println(n.Load())
}
На экране "100": каждое приращение
доходит, хотя отдельного замка в коде
нет.
Чтение после накопления:
package main
import (
"fmt"
"sync/atomic"
)
func main() {
var total atomic.Int64
total.Add(3)
total.Add(2)
fmt.Println(total.Load())
}
Дан следующий код:
var hits atomic.Int64
var wg sync.WaitGroup
func bump(wg *sync.WaitGroup) {
defer wg.Done()
hits.Add(1)
}
Запустите bump двадцать раз
с ожиданием группы и выведите итог
через чтение счетчика.
Дан следующий код:
var k int
var wg sync.WaitGroup
func step(wg *sync.WaitGroup) {
defer wg.Done()
k++
}
Замените обычное целое на атомарный счетчик и добейтесь точного итога при пятидесяти ветках.
Скажите, когда для одного числа удобнее атомарный счетчик, а когда все же нужен замок вокруг нескольких полей.