No, este código es absolutamente, demostrablemente no inseguro.
import threading
i = 0
def test():
global i
for x in range(100000):
i += 1
threads = [threading.Thread(target=test) for t in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
assert i == 1000000, i
falla constantemente.
i + = 1 tiene por cuatro códigos de operación: carga i, la carga 1, añadir los dos, y almacenar de nuevo a i. El intérprete de Python conmuta los hilos activos (liberando el GIL de un hilo para que otro hilo pueda tenerlo) cada 100 códigos de operación. (Ambos son detalles de implementación). La condición de carrera ocurre cuando la preferencia de 100 opcode ocurre entre la carga y el almacenamiento, permitiendo que otro subproceso comience a incrementar el contador. Cuando vuelve al hilo suspendido, continúa con el valor anterior de "i" y mientras tanto deshace los incrementos ejecutados por otros hilos.
Por lo que es multi-hilo es sencillo; añadir un bloqueo:
#!/usr/bin/python
import threading
i = 0
i_lock = threading.Lock()
def test():
global i
i_lock.acquire()
try:
for x in range(100000):
i += 1
finally:
i_lock.release()
threads = [threading.Thread(target=test) for t in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
assert i == 1000000, i
Debe haber una desaceleración 'c' mundial al inicio de cada función o esto realmente no hacer nada. – JoshB