Go previnindo data race · Cesar Gimenes
Como detectar e prevenir data race com Golang
No projeto do Ricardo Gomes onde ganhamos uma bela performace também acabamos acidentalmente incluindo uma bela data race.
Data race acontece quando você tem acessos simultâneos a mesma variável por duas threads diferentes.
Mas Go tem ferramentas para tudo, e nessa brincadeira eu aprendi sobre o parâmetro -race. Esse parâmetro faz com que o Go procure por possíveis data races em seu código e pode ser usado com test, run, build e install.
Testando o Data Race Detector
Vamos supor o seguinte código.
Agora tente rodar ele passando o parâmetro -race para o run.
A saída vai ser como abaixo:
> $ go run -race main.go
Read at 0x00c42007c000 by goroutine 5:
/Users/cesar/test/main.go:7 +0x3b
Previous write at 0x00c42007c000 by main goroutine:
/Users/cesar/test/main.go:10 +0xa7
/Users/cesar/test/main.go:8 +0x7d
E como esperado Go gerou um alerta avisando do data race mostrando as linhas, as goroutines, etc.
Agora vamos modificar o código para resolver o problema. Poderíamos usar canais nesse caso mas a forma mais simples e parecida com outras linguagens seria fazer um lock e unlock sempre que for ler e escrever nos recursos compartilhados entre as treads.
Veja o exemplo do código corrigido:
import "sync"
E teste com go run -race main.go como fizemos anteriormente, você vai ver que o alerta sumiu já que agora não tem mais o perigo de data race.
É impressionante a quantidade de ferramentas de análise estática que vem de fabrica no Go.
Um ultimo detalhe, esse código vai sempre retornar 1 no valor de X, isso porque a função main acaba rápido demais e Go limpa as goroutines assim que a função main terminar, então a nossa goroutine nunca vai ter tempo de adicionar nada em x, eu usei esse exemplo apenas para ilustrar usando o mínimo de código possível.