Python Language
Trabajando alrededor del bloqueo global de intérpretes (GIL)
Buscar..
Observaciones
¿Por qué hay un GIL?
El GIL ha existido en CPython desde el inicio de los hilos de Python, en 1992. Está diseñado para garantizar la seguridad de los hilos al ejecutar el código de Python. Los intérpretes de Python escritos con un GIL impiden que varios subprocesos nativos ejecuten códigos de byte de Python a la vez. Esto facilita que los complementos se aseguren de que su código sea seguro para subprocesos: simplemente bloquee la GIL, y solo su subproceso activo puede ejecutarse, por lo que su código es automáticamente seguro para subprocesos.Versión corta: la GIL garantiza que no importa cuántos procesadores y subprocesos tenga, solo se ejecutará un subproceso de un intérprete de Python al mismo tiempo.
Esto tiene muchos beneficios de facilidad de uso, pero también tiene muchos beneficios negativos.
Tenga en cuenta que un GIL no es un requisito del lenguaje Python. Por consiguiente, no puede acceder a la GIL directamente desde el código estándar de Python. No todas las implementaciones de Python utilizan un GIL.
Intérpretes que tienen un GIL: CPython, PyPy, Cython (pero puede desactivar el GIL con nogil
)
Intérpretes que no tienen un GIL: Jython, IronPython
Detalles sobre cómo funciona el GIL:
Cuando se está ejecutando un hilo, bloquea la GIL. Cuando un hilo quiere ejecutarse, solicita el GIL y espera hasta que esté disponible. En CPython, antes de la versión 3.2, el hilo en ejecución verificaba después de un cierto número de instrucciones de Python para ver si otro código quería el bloqueo (es decir, liberó el bloqueo y luego lo solicitó nuevamente). Este método tendía a provocar la hambruna de hilos, en gran parte porque el hilo que liberaba el bloqueo lo volvería a adquirir antes de que los hilos en espera tuvieran la oportunidad de despertarse. Desde la versión 3.2, los subprocesos que desean que el GIL espere el bloqueo por algún tiempo, y después de ese tiempo, establecen una variable compartida que obliga al hilo en ejecución a ceder. Sin embargo, esto todavía puede resultar en tiempos de ejecución drásticamente más largos. Consulte los siguientes enlaces en dabeaz.com (en la sección de referencias) para obtener más detalles.CPython libera automáticamente la GIL cuando un hilo realiza una operación de E / S. Las bibliotecas de procesamiento de imágenes y las operaciones de procesamiento de números numpy liberan la GIL antes de realizar su procesamiento.
Beneficios de la GIL
Para los intérpretes que usan la GIL, la GIL es sistémica. Se utiliza para preservar el estado de la aplicación. Beneficios incluidos:- Recolección de basura: los recuentos de referencia seguros para subprocesos deben modificarse mientras la GIL está bloqueada. En CPython, toda la colección de garbarge está vinculada a la GIL. Este es un grande; vea el artículo de la wiki de python.org sobre la GIL (que se encuentra en las Referencias, a continuación) para obtener detalles sobre lo que aún debe ser funcional si se desea eliminar la GIL.
- Facilidad para los programadores que tratan con GIL: bloquear todo es simplista, pero fácil de codificar
- Facilita la importación de módulos desde otros idiomas.
Consecuencias de la GIL
La GIL solo permite que un hilo ejecute el código de Python a la vez dentro del intérprete de python. Esto significa que el multihilo de procesos que ejecutan código de Python estricto simplemente no funciona. Al usar subprocesos contra GIL, es probable que tenga un peor rendimiento con los subprocesos que si se ejecutara en un solo subproceso.Referencias:
https://wiki.python.org/moin/GlobalInterpreterLock - resumen rápido de lo que hace, detalles finos sobre todos los beneficios
http://programmers.stackexchange.com/questions/186889/why-was-python-written-with-the-gil - resumen claramente escrito
http://www.dabeaz.com/python/UnderstandingGIL.pdf : cómo funciona GIL y por qué se ralentiza en varios núcleos
http://www.dabeaz.com/GIL/gilvis/index.html : visualización de los datos que muestran cómo GIL bloquea los subprocesos
http://jeffknupp.com/blog/2012/03/31/pythons-hardest-problem/ - fácil de entender la historia del problema GIL
https://jeffknupp.com/blog/2013/06/30/pythons-hardest-problem-revisited/ - detalles sobre las formas de solucionar las limitaciones de la GIL
Multiprocesamiento.Pool
La respuesta simple, cuando se pregunta cómo usar hilos en Python es: "No. Utilice procesos, en su lugar". El módulo de multiprocesamiento le permite crear procesos con una sintaxis similar a la creación de subprocesos, pero prefiero usar su conveniente objeto Pool.
Usando el código que David Beazley utilizó por primera vez para mostrar los peligros de los hilos en contra de la GIL , lo reescribiremos usando multiprocesamiento .
Código de David Beazley que mostraba problemas de subprocesos de GIL
from threading import Thread
import time
def countdown(n):
while n > 0:
n -= 1
COUNT = 10000000
t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
Reescrita utilizando multiproceso. import multiprocessing
import time
def countdown(n):
while n > 0:
n -= 1
COUNT = 10000000
start = time.time()
with multiprocessing.Pool as pool:
pool.map(countdown, [COUNT/2, COUNT/2])
pool.close()
pool.join()
end = time.time()
print(end-start)
En lugar de crear hilos, esto crea nuevos procesos. Dado que cada proceso es su propio intérprete, no hay colisiones de GIL. multiprocessing.Pool abrirá tantos procesos como núcleos haya en la máquina, aunque en el ejemplo anterior solo necesitaría dos. En un escenario del mundo real, desea diseñar su lista para que tenga al menos la misma longitud que procesadores en su máquina. El Pool ejecutará la función que le indica que ejecute con cada argumento, hasta el número de procesos que cree. Cuando la función finalice, cualquier función restante en la lista se ejecutará en ese proceso.
Descubrí que, incluso utilizando la instrucción with
, si no cierra y se une al grupo, los procesos continúan existiendo. Para limpiar recursos, siempre cierro y me uno a mis piscinas.
Cython Nogil:
Cython es un intérprete alternativo de python. Utiliza el GIL, pero te permite deshabilitarlo. Ver su documentación
Como ejemplo, usando el código que David Beazley usó por primera vez para mostrar los peligros de los hilos contra la GIL , lo reescribiremos usando nogil:
Código de David Beazley que mostraba problemas de subprocesos de GIL
from threading import Thread
import time
def countdown(n):
while n > 0:
n -= 1
COUNT = 10000000
t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
Reescrito usando nogil (SOLO FUNCIONA EN CYTHON):
from threading import Thread
import time
def countdown(n):
while n > 0:
n -= 1
COUNT = 10000000
with nogil:
t1 = Thread(target=countdown,args=(COUNT/2,))
t2 = Thread(target=countdown,args=(COUNT/2,))
start = time.time()
t1.start();t2.start()
t1.join();t2.join()
end = time.time()
print end-start
Es así de simple, siempre y cuando estés usando cython. Tenga en cuenta que la documentación indica que debe asegurarse de no cambiar ningún objeto de Python:
El código en el cuerpo de la declaración no debe manipular los objetos de Python de ninguna manera, y no debe llamar a nada que manipule los objetos de Python sin volver a adquirir la GIL. Cython actualmente no comprueba esto.