Introdução
Este laboratório foca no gerenciamento de estado em Go usando o pacote sync/atomic para contadores atômicos acessados por múltiplas goroutines.
Contadores Atômicos
O problema é incrementar um contador exatamente 1000 vezes usando 50 goroutines e o pacote sync/atomic.
- Use o pacote
sync/atomicpara incrementar o contador. - Use um
WaitGrouppara esperar que todas as goroutines terminem seu trabalho.
## Esperamos obter exatamente 50.000 operações. Se tivéssemos
## usado o `ops++` não atômico para incrementar o contador,
## provavelmente obteríamos um número diferente, mudando entre
## as execuções, porque as goroutines interfeririam
## umas com as outras. Além disso, obteríamos falhas de data race
## ao executar com a flag `-race`.
$ go run atomic-counters.go
ops: 50000
## Em seguida, veremos mutexes, outra ferramenta para gerenciar
## estado.
Aqui está o código completo:
// O principal mecanismo para gerenciar o estado em Go é
// a comunicação por meio de canais. Vimos isso, por exemplo,
// com [pools de trabalhadores](worker-pools). Existem algumas outras
// opções para gerenciar o estado, no entanto. Aqui, vamos
// analisar o uso do pacote `sync/atomic` para _contadores
// atômicos_ acessados por múltiplas goroutines.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
// Usaremos um inteiro sem sinal para representar nosso
// contador (sempre positivo).
var ops uint64
// Um WaitGroup nos ajudará a esperar que todas as goroutines
// terminem seu trabalho.
var wg sync.WaitGroup
// Iniciaremos 50 goroutines que cada uma incrementa o
// contador exatamente 1000 vezes.
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
for c := 0; c < 1000; c++ {
// Para incrementar atomicamente o contador, nós
// usamos `AddUint64`, dando a ele o endereço de memória
// do nosso contador `ops` com a sintaxe `&`.
atomic.AddUint64(&ops, 1)
}
wg.Done()
}()
}
// Espere até que todas as goroutines terminem.
wg.Wait()
// É seguro acessar `ops` agora porque sabemos
// que nenhuma outra goroutine está escrevendo nele. Lendo
// atômicos com segurança enquanto eles estão sendo atualizados é
// também possível, usando funções como
// `atomic.LoadUint64`.
fmt.Println("ops:", ops)
}
Resumo
Neste laboratório, aprendemos como usar o pacote sync/atomic para gerenciar o estado em Go, incrementando um contador usando múltiplas goroutines. A função AddUint64 foi usada para incrementar atomicamente o contador, e um WaitGroup foi usado para esperar que todas as goroutines terminassem seu trabalho.