Atomic Counters
#
// O mecanismo primário para gerenciar estado em Go ẽ
// a comunicação entre canais. Um dos exemplos apresentados
// foi os [worker pools](worker-pools). No entanto, existem
// algumas outras opções para gerenciar estado.
// Aqui será apresentado o pacote `sync/atomic` para _contadores
// atômicos_ accessados por múltiplas goroutines.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// Será utilizado um inteiro _unsigned integer_
// para representar um contador sempre positivo.
var ops uint64
// Um WaitGroup auxiliará a aguardar todas as goroutines
// finalizarem suas tarefas.
var wg sync.WaitGroup
// Iniciaremos 50 goroutines que incrementarão o contador
// exatamente 1000 vezes.
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
for c := 0; c < 1000; c++ {
// Para incrementar o contador será utilizado
// `AddUint64`, passando como parâmetro o
// endereço de memória do contador `ops`.
// Note que para acessar o endereço de memória
// de determinado dado, se utiliza a sintaxe `&`.
atomic.AddUint64(&ops, 1)
// ops++
}
wg.Done()
}()
}
// Aguarda todas as goroutines finalizarem.
wg.Wait()
// É seguro ler o contador `ops` uma vez que não
// há nenhuma goroutine escrevendo sobre ele.
// Para ler o contador enquanto ele ẽ atualizado,
// pode-se utilizar a função `atomic.LoadUint64`,
// por exemplo.
fmt.Println("ops:", ops)
}
# É esperado que o código realize exatamente
# 50.000 operações.
# Se fosse utilizada a forma não-atômica de
# incrementação, provavelmente o resultado
# seria diferente entre as execuções, porque
# as goroutines interfeririam umas com as
# outras. Alẽm disso, provavelmente aconteceria
# falhas de data race, que é possível visualizar
# executando o código com a flag `-race`
# (`go run -race atomic-counters.go`)
$ go run atomic-counters.go
ops: 50000
# Código com `ops++` ao invés de
# `atomic.AddUint64(&ops, 1)`
# sendo executado com a flag `-race`
$ go run -race atomic-counters.go
==================
WARNING: DATA RACE
Read at 0x00c00001a0f8 by goroutine 10:
main.main.func1()
/.../atomic-counters.go:38 +0x46
==================
ops: 6573
Found 1 data race(s)
exit status 66
# Em seguida, será apresentado mutexes,
# outra ferramenta para gerenciar estado.