Condição de Corrida (Race Condition)
Definição
Condição de Corrida (Race Condition) é uma falha que ocorre quando duas ou mais threads ou processos acessam e manipulam recursos compartilhados ao mesmo tempo, de forma não controlada. Isso pode levar a resultados incorretos, imprevisíveis ou inconsistentes, pois a ordem de execução influencia diretamente o comportamento do programa.
Situações de condição de corrida geralmente ocorrem quando múltiplas unidades de execução:
- Lêem e escrevem na mesma variável;
- Não utilizam mecanismos de sincronização (como locks ou semáforos);
- Dependem da ordem de execução para funcionar corretamente.
Condições de corrida nem sempre ocorrem em todas as execuções — isso torna o bug ainda mais perigoso e difícil de identificar em testes.
Por que a Condição de Corrida é um problema
- Identificar situações perigosas em sistemas concorrentes.
- Evitar falhas sutis e intermitentes que surgem apenas em determinadas condições de execução.
- Promover o uso de boas práticas de sincronização e controle de acesso a recursos compartilhados.
Exemplo prático (com condição de corrida)
Este código simula duas threads modificando a mesma variável contador
, sem proteção:
import threading
contador = 0
def incrementar():
global contador
for _ in range(100000):
contador += 1
t1 = threading.Thread(target=incrementar)
t2 = threading.Thread(target=incrementar)
t1.start()
t2.start()
t1.join()
t2.join()
print("Valor final:", contador)
Saída esperada
Valor final: 200000 # (esperado)
Saída real (exemplo de condição de corrida)
Valor final: 182391 # ou outro valor incorreto
O valor final geralmente será menor que 200000, pois as threads podem sobrescrever os valores entre si — um clássico exemplo de condição de corrida.
O ciclo da Condição de Corrida
[Thread A lê contador = 10]
[Thread B lê contador = 10]
[Thread A escreve contador = 11]
[Thread B escreve contador = 11] <-- sobrescreve valor anterior
- As leituras e escritas ocorrem sem coordenação.
- Resultado: atualizações se perdem.
Como evitar
Use um lock (trava) para garantir que apenas uma thread modifique a variável por vez:
import threading
contador = 0
lock = threading.Lock()
def incrementar():
global contador
for _ in range(100000):
with lock:
contador += 1
Com o uso de with lock
, o acesso ao contador torna-se exclusivo por thread, evitando interferências.