En primer lugar, los bloqueos están diseñados para proteger los recursos; los hilos no están 'bloqueados' o 'desbloqueados' ellos/adquirir/un bloqueo (en un recurso) y/liberar/un bloqueo (en un recurso).
Tiene razón en que desea hilos se ejecuten simultáneamente tanto como sea posible, pero vamos a echar un vistazo a esto:
y=10
def doStuff(x):
global y
a = 2 * y
b = y/5
y = a + b + x
print y
t1 = threading.Thread(target=doStuff, args=(8,))
t2 = threading.Thread(target=doStuff, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
Ahora, es posible saber que ninguno de estos temas podría completar e imprimir primero . Esperaría ver tanto la salida 30.
Pero es posible que no.
y es un recurso compartido, y en este caso, los bits que leen y escriben a y son parte de lo que se llama una "sección crítica" y deberían estar protegidos por un bloqueo. La razón es que no obtienes unidades de trabajo: cualquiera de los hilos puede obtener CPU en cualquier momento.
Piense en ello como esto:
t1 está ejecutando felizmente código y se realiza
a = 2 * y
Ahora T1 tiene a = 20 y deja de ejecutarse durante un tiempo. t2 se activa mientras t1 espera más tiempo de CPU. t2 ejecuta:
a = 2 * y
b = y/5
y = a + b + x
en este punto la variable global y = 30
t2 detiene paradas por un poco y t1 recoge de nuevo. se ejecuta:
b = y/5
y = a + b + x
Puesto que y era 30 cuando se estableció b, b = 6 e y se establece ahora en 34.
el orden de las impresiones no es determinista, así y es posible obtener la 30 primero o el 34 primero.
usando una cerradura tendríamos:
global l
l = threading.Lock()
def doStuff(x):
global y
global l
l.acquire()
a = 2 * y
b = y/5
y = a + b + x
print y
l.release()
Esto necesariamente hace que esta sección del código lineal - sólo un hilo a la vez. Pero si todo tu programa es secuencial, no deberías estar usando subprocesos de todos modos. La idea es que ganes velocidad en función del porcentaje de código que tienes, que puede ejecutar bloqueos externos y ejecutarse en paralelo. Esta es (una de las razones) por la que el uso de subprocesos en un sistema de 2 núcleos no duplica el rendimiento para todo.
el propio bloqueo también es un recurso compartido, pero debe ser: una vez que un hilo adquiere el bloqueo, todos los otros hilos que intenten adquirir el/mismo/bloqueo se bloquearán hasta que se libere. Una vez que se libera, el primer hilo para avanzar y adquirir el bloqueo bloqueará todos los demás hilos de espera.
¡Espero que eso sea suficiente para continuar!
No, no estoy preguntando sobre el GIL, conozco las limitaciones del mismo en python y estoy contento con él, la cuestión es bloquear hilos con acquire() y release() no relacionados con el GIL (aparte de tiene un candado en el nombre) – MistahX
Reetiquetado, como no exclusivo de 'python' en absoluto. –
reticulado como python, me refiero a los métodos de bloqueo en el módulo de subprocesamiento, se olvidó de agregar eso en – MistahX