O que é Race Condition?

Alan Nunes
Comunidade XP

--

(Foto de Pixabay, por Stockvault)

Race condition é um problema comum que acontece em programação concorrente, onde duas threads modificam o mesmo recurso simultaneamente, podendo gerar resultados inesperados.

Um exemplo prático: Conta Bancária

Imagine um banco digital, que ao realizar débito em conta, verifica se você possui saldo suficiente. Veja o modelo abaixo:

Para garantir que está tudo certo, escrevemos um teste unitário:

Executamos o teste e verificamos que o Débito em conta está funcionando corretamente.

Teste unitário da ContaBancaria

Agora que sabemos como o débito em conta funciona, vamos criar um cenário hipotético onde você possui R$ 2000,00 em conta e tenta realizar a compra de um celular (R$ 1500) e uma geladeira (R$ 1700) exatamente ao mesmo tempo.

O resultado esperado é que uma das compras seja negada por saldo insuficiente. E para simular isso, criamos o seguinte programa:

Ao executar temos o seguinte resultado:

Exemplo race condition acontecendo na conta bancária

Sim, não aconteceu conforme o esperado. O programa permitiu a compra, mesmo não tendo saldo suficiente em conta.

Explicando a causa

Mas por que raios isso aconteceu, sendo que nosso teste unitário garantiu que isso não ocorreria?

Antes, vamos entender como o programa executou as compras:

  1. Saldo na conta: R$ 2000,00
  2. Inicia débito em conta de R$ 1500,00 do Celular
  3. Inicia débito em conta de R$ 1700,00 da Geladeira
  4. Saldo em conta é suficiente (Celular)? Sim, saldo de R$ 2000,00
  5. Saldo em conta é suficiente (Geladeira)? Sim, saldo de R$ 2000,00
  6. Saldo suficiente, então realiza o débito (Celular)
  7. Saldo atual R$ 500,00
  8. Saldo suficiente, então realiza o débito (Geladeira)
  9. Saldo atual R$ -1200,00

Resumindo, as duas compras olharam para o mesmo saldo de R$ 2000,00, porque acessaram o mesmo recurso simultaneamente, antes de ocorrer o débito da outra compra. E por isso, passaram com sucesso pela etapa de validação de saldo.

Verificação de saldo suficiente

Como resolver o problema?

Permitir que apenas uma compra debite da conta bancária por vez.

Se duas compras são feitas simultaneamente, uma deve iniciar o processo de débito e a outra deve esperar ela terminar para depois começar.

Para atingir esse objetivo, nós podemos usar um recurso chamado de “lock” que tranca o objeto enquanto ele está sendo usado por alguém. E quando esse alguém terminar de usar, libera o acesso para quem estava esperando.

Então, a forma correta de debitar da conta bancária usando “lock” seria assim:

Ao executar o programa novamente temos o seguinte resultado:

Usando lock ao debitar da conta

Conclusão

Ao usar programação concorrente devemos estar sempre atentos a esses tipos de cenários e evitar sérios problemas, como o caso da conta bancária.

Ao criar um novo recurso, pergunte-se “se mais de uma thread acessarem esse recurso simultaneamente, terei problemas?”

Se a resposta for sim, avalie se é necessário aplicar algum tipo de lock.

--

--