Regra
Garantir thread-safe acesso a partilhado partilhado.
Partilhado mutável estado acedido por múltiplas threads
sem sincronização causa corrida condições e tempo de execução erros.
Linguagens suportadas: Python, Java, C#Introdução
Quando múltiplas threads acessam e modificam variáveis compartilhadas sem sincronização, ocorrem condições de corrida (race conditions). O valor final depende do tempo de execução imprevisível das threads, levando à corrupção de dados, cálculos incorretos ou erros de tempo de execução. Um contador incrementado por múltiplas threads sem bloqueio perderá atualizações, pois as threads leem valores desatualizados, os incrementam e escrevem resultados conflitantes.
Por que isso importa
Corrupção de dados e resultados incorretos: Condições de corrida causam corrupção silenciosa de dados, onde os valores se tornam inconsistentes ou incorretos. Saldos de contas podem estar errados, contagens de estoque podem ser negativas ou estatísticas agregadas podem ser corrompidas. Esses bugs são difíceis de reproduzir porque dependem do tempo exato de execução dos threads.
Instabilidade do sistema: Acesso não sincronizado a estados compartilhados pode travar aplicações. Uma thread pode modificar uma estrutura de dados enquanto outra a lê, causando exceções como erros de ponteiro nulo ou índice fora dos limites. Em produção, isso se manifesta como travamentos intermitentes sob carga.
Complexidade de depuração: Condições de corrida são notoriamente difíceis de depurar porque são não-determinísticas. O bug pode não aparecer em testes single-threaded ou em ambientes de baixa carga. A reprodução exige um entrelaçamento de threads específico que é difícil de forçar, fazendo com que os problemas apareçam e desapareçam aleatoriamente.
Exemplos de código
❌ Não-conforme:
class BankAccount:
def __init__(self):
self.balance = 0
def deposit(self, amount):
current = self.balance
# Condição de corrida: outra thread pode modificar o saldo aqui
time.sleep(0.001) # Simula tempo de processamento
self.balance = current + amount
def withdraw(self, amount):
if self.balance >= amount:
current = self.balance
time.sleep(0.001)
self.balance = current - amount
return True
return False
Por que está errado: Múltiplas threads chamando deposit() ou withdraw() simultaneamente criam condições de corrida. Duas threads depositando $100 cada podem ambas ler o saldo como $0, então ambas escrevem $100, resultando em um saldo final de $100 em vez de $200.
✅ Compatível:
import threading
class BankAccount:
def __init__(self):
self.__balance = 0
self.__lock = threading.Lock()
@property
def balance(self):
with self.__lock:
return self.__balance
def deposit(self, amount):
with self.__lock:
current = self.__balance
time.sleep(0.001)
self.__balance = current + amount
def withdraw(self, amount):
with self.__lock:
if self.__balance >= amount:
current = self.__balance
time.sleep(0.001)
self.__balance = current - amount
return True
return False
Por que isso importa: O threading.Lock() garante que apenas uma thread acesse o saldo por vez. Quando uma thread detém o lock, as outras esperam, impedindo modificações simultâneas. Privado __balance com somente leitura @property impede que código externo ignore a proteção de bloqueio.
Conclusão
Proteja todo o estado mutável compartilhado com primitivas de sincronização apropriadas, como locks, semáforos ou operações atômicas. Prefira estruturas de dados imutáveis ou armazenamento thread-local sempre que possível. Quando a sincronização for necessária, minimize as seções críticas para reduzir a contenção e melhorar o desempenho.
.avif)
