Regla
Asegúrese de que hilo seguro acceso a compartido compartido.
Compartido mutable compartido accesible en a través de múltiples hilos
sin sincronización causa carrera conditions y tiempo de ejecución errores.
Lenguajes compatibles: Python, Java, C#Introducción
Cuando varios hilos acceden y modifican variables compartidas sin sincronización, se producen condiciones de carrera. El valor final depende del tiempo de ejecución impredecible de los hilos, lo que lleva a la corrupción de datos, cálculos incorrectos o errores en tiempo de ejecución. Un contador incrementado por varios subprocesos sin bloqueo perderá actualizaciones cuando los subprocesos lean valores obsoletos, los incrementen y escriban resultados contradictorios.
Por qué es importante
Corrupción de datos y resultados incorrectos: Las condiciones de carrera provocan una corrupción silenciosa de los datos en la que los valores se vuelven incoherentes o incorrectos. Los saldos de las cuentas pueden ser erróneos, los recuentos de inventario pueden ser negativos o las estadísticas agregadas pueden estar corruptas. Estos errores son difíciles de reproducir porque dependen de la sincronización exacta de los hilos.
Inestabilidad del sistema: El acceso no sincronizado al estado compartido puede bloquear las aplicaciones. Un hilo puede modificar una estructura de datos mientras otro la lee, provocando excepciones como errores de puntero nulo o índices fuera de los límites. En producción, esto se manifiesta como fallos intermitentes bajo carga.
Complejidad de la depuración: Las condiciones de carrera son notoriamente difíciles de depurar porque no son deterministas. El fallo puede no aparecer en pruebas con un único hilo o en entornos de baja carga. La reproducción requiere un entrelazado de hilos específico que es difícil de forzar, lo que hace que los problemas aparezcan y desaparezcan aleatoriamente.
Ejemplos de códigos
❌ No conforme:
class CuentaBancaria:
def __init__(self):
self.balance = 0
def deposit(self, amount):
current = self.balance
# Condición de carrera: otro hilo puede modificar el saldo aquí
time.sleep(0.001) # Simula el tiempo de procesamiento
self.balance = current + amount
def retirar(self, importe):
if self.saldo >= importe:
current = self.balance
time.sleep(0.001)
auto.saldo = actual - importe
return True
return False
Por qué está mal: Múltiples hilos llamando a depositar() o retirar() simultáneamente crean condiciones de carrera. Dos hilos depositando $100 cada uno podrían leer el saldo como $0, luego ambos escribir $100, resultando en un saldo final de $100 en lugar de $200.
✅ Conforme:
importar roscado
class BankAccount:
def __init__(self):
self.__balance = 0
self.__lock = threading.Lock()
@property
def balance(self):
with self.__lock:
return self.__balance
def depósito(self, importe):
con self.__lock:
current = self.__balance
time.sleep(0.001)
self.__balance = current + amount
def retirar(self, importe):
con self.__lock:
if self.__balance >= amount:
current = auto.__balance
time.sleep(0.001)
auto.__balance = actual - importe
return True
return False
Por qué es importante: En threading.Lock() garantiza que sólo un hilo acceda al saldo a la vez. Cuando un hilo mantiene el bloqueo, los demás esperan, evitando modificaciones simultáneas. Privado __balance con sólo lectura @propiedad impide que un código externo anule la protección de la cerradura.
Conclusión
Proteja todo el estado mutable compartido con primitivas de sincronización apropiadas como bloqueos, semáforos u operaciones atómicas. Siempre que sea posible, prefiera estructuras de datos inmutables o almacenamiento local. Cuando la sincronización sea necesaria, minimice las secciones críticas para reducir la contención y mejorar el rendimiento.
.avif)
