Existem basicamente duas formas para resolver problemas de race condition. Mutex e Channels.
Se você não está familiarizado com o termo race condition, não se preocupe. Esse termo é utilizado para descrever um pedaço do código que será executado por múltiplas goroutines e que, a cada execução, seu resultado pode variar devido a forma como o Go alterna a execução entre goroutines.
Para ficar um pouco mais claro, vamos dar uma olhada no código abaixo.
Esse código vai iniciar 1000 goroutines. Cada goroutine vai incrementar a variável total em 1. Ao final o valor total será exibido.
Se esse código fosse executado sem goroutines, obviamente que no final o valor da variável total
seria 1000. No entanto, como você pode ver no print abaixo, o fato de usar goroutines muda tudo.
Como eu disse no inicio, devido a forma como o Go alterna entre a execução das goroutines não ser idêntico a cada execução, o resultado final obtido nas execuções, além de incorreto foi diferente.
Isso acontece por que o acesso a variável total
está sendo compartilhada entre as goroutines, o que acaba fazendo com que o resultado final seja diferente do esperado.
Para corrigir isso, precisamos fazer uma espécie de semáforo, para que, quando uma goroutine estiver acessando a variável total
as outras goroutines aguardem “sua vez”.
No código abaixo, podemos ver a utilização de Mutex para corrigir o problema.
Quando utilizamos mutex, qualquer parte de código entre os métodos Lock
e Unlock
ficam bloqueados para outras goroutines. Somente depois que o método Unlock
for executado é que uma segunda goroutine pode acessar as variáveis ali contidas.
Se executarmos o nosso programa novamente, vamos ver o seguinte resultado:
Outra forma para resolver o problema de race condition seria utilizando channels, como podemos ver no exemplo abaixo:
Como nosso channel só tem uma posição, ao escrever true nele, bloqueamos outras goroutines de continuarem sua execução.
Ok, mas se os dois fazem a mesma coisa, qual é a melhor forma para evitar race condition?
Embora muitos desenvolvedores, principalmente os que estão iniciando agora na linguagem, gostem muito de resolver tudo com channels, vou deixar uma pequena dica aqui.
Use channels somente quando uma goroutine precisar se comunicar com outra. Se o desejado for um semáforo para controlar acesso a partes sucetíveis a race condition dentro de uma mesma função, utilize mutex.
Deixem suas dúvidas nos comentários.
Até a próxima!