Go previnindo data race
Uma data race acontece quando você tem acessos simultâneos a uma mesma variável por duas threads diferentes.
No projeto do Ricardo Gomes, onde ganhamos uma bela performace, também acabamos acidentalmente incluindo uma bela data race.
Uma data race acontece quando você tem acessos simultâneos a uma 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:
package main
func main() {
var x int
go func() {
x++
}()
x++
println(x)
}
Agora, tente rodá-lo passando o parâmetro -race para o run.
go run -race main.go
A saída vai ser como abaixo:
> $ go run -race main.go
1
==================
WARNING: DATA RACE
Read at 0x00c42007c000 by goroutine 5:
main.main.func1()
/Users/cesar/test/main.go:7 +0x3b
Previous write at 0x00c42007c000 by main goroutine:
main.main()
/Users/cesar/test/main.go:10 +0xa7
Goroutine 5 (running) created at:
main.main()
/Users/cesar/test/main.go:8 +0x7d
==================
Found 1 data race(s)
exit status 66
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:
package main
import "sync"
func main() {
var x int
var m sync.Mutex
go func() {
m.Lock()
x++
m.Unlock()
}()
m.Lock()
x++
println(x)
m.Unlock()
}
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 último detalhe: esse código vai sempre retornar 1 no valor de X, isso porque a função main acaba rápido demais e o 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.








