¿Por qué el locking global de intérpretes?

¿Cuál es exactamente la función de Global Interpreter Lock de Python? ¿Los otros lenguajes que se comstackn a bytecode emplean un mecanismo similar?

En general, para cualquier problema de seguridad de subprocesos, deberá proteger sus estructuras de datos internas con lockings. Esto se puede hacer con varios niveles de granularidad.

  • Puede utilizar el locking de grano fino, donde cada estructura tiene su propio locking.

  • Puede usar un locking de grano grueso donde un locking protege todo (el enfoque GIL).

Hay varios pros y contras de cada método. El locking de grano fino permite un mayor paralelismo: dos subprocesos se pueden ejecutar en paralelo cuando no comparten ningún recurso. Sin embargo, hay una sobrecarga administrativa mucho mayor. Para cada línea de código, es posible que necesite adquirir y liberar varios lockings.

El enfoque de grano grueso es el opuesto. Dos subprocesos no pueden ejecutarse al mismo tiempo, pero un subproceso individual se ejecutará más rápido porque no hace mucha contabilidad. En última instancia, se trata de una compensación entre la velocidad de un solo hilo y el paralelismo.

Ha habido algunos bashs de eliminar el GIL en python, pero la sobrecarga adicional para máquinas de un solo hilo fue generalmente demasiado grande. Algunos casos pueden ser más lentos incluso en máquinas con varios procesadores debido a la contención de locking.

¿Los otros lenguajes que se comstackn a bytecode emplean un mecanismo similar?

Varía, y probablemente no debería considerarse tanto una propiedad de lenguaje como una propiedad de implementación. Por ejemplo, hay implementaciones de Python como Jython y IronPython que utilizan el enfoque de subprocesamiento de su VM subyacente, en lugar de un enfoque GIL. Además, la próxima versión de Ruby parece estar avanzando hacia la introducción de un GIL.

Lo siguiente es del Manual de referencia oficial de Python / C API :

El intérprete de Python no es completamente seguro para subprocesos. Para admitir progtwigs de Python de subprocesos múltiples, hay un locking global que debe ser mantenido por el hilo actual antes de poder acceder de forma segura a los objetos de Python. Sin el locking, incluso las operaciones más simples podrían causar problemas en un progtwig de subprocesos múltiples: por ejemplo, cuando dos subprocesos incrementan simultáneamente el recuento de referencias del mismo objeto, el recuento de referencias podría incrementarse solo una vez en lugar de dos veces.

Por lo tanto, existe la regla de que solo el subproceso que ha adquirido el locking global del intérprete puede operar en los objetos de Python o llamar a las funciones de la API de Python / C. Para admitir los progtwigs Python de subprocesos múltiples, el intérprete libera y vuelve a adquirir el locking de forma predeterminada, cada 100 instrucciones de bytecode (esto puede cambiarse con sys.setcheckinterval ()). El locking también se libera y se vuelve a adquirir en torno a operaciones de E / S potencialmente bloqueadas, como leer o escribir un archivo, de modo que otros subprocesos puedan ejecutarse mientras el subproceso que solicita la E / S está esperando a que se complete la operación de E / S.

Creo que resume el problema bastante bien.

El locking global del intérprete es un gran locking de tipo mutex que protege a los contadores de referencia para que no se puedan colocar. Si está escribiendo código Python puro, todo esto sucede detrás de la escena, pero si está incrustando Python en C, es posible que tenga que tomar / liberar explícitamente el locking.

Este mecanismo no está relacionado con la comstackción de Python a bytecode. No es necesario para Java. De hecho, ni siquiera es necesario para Jython (python comstackdo para jvm).

vea también esta pregunta

Python, al igual que Perl 5, no fue diseñado desde cero para ser seguro para la ejecución de subprocesos. Los hilos fueron injertados después del hecho, por lo que el locking global del intérprete se utiliza para mantener la exclusión mutua en donde solo un hilo está ejecutando código en un momento dado en las entrañas del intérprete.

Los hilos individuales de Python son multitarea de forma cooperativa por el propio intérprete, alternando el locking cada cierto tiempo.

Es necesario agarrar el candado cuando está hablando con Python desde C cuando otros subprocesos de Python están activos para ‘participar’ en este protocolo y asegurarse de que no haya nada peligroso detrás de su espalda.

Otros sistemas que tienen una herencia de un solo subproceso que más tarde evolucionó a sistemas multihilo a menudo tienen algún mecanismo de este tipo. Por ejemplo, el kernel de Linux tiene el “Gran Bloqueo del Kernel” desde sus primeros días de SMP. Gradualmente, con el tiempo, a medida que el rendimiento de subprocesos múltiples se convierte en un problema, hay una tendencia a tratar de dividir estos tipos de lockings en piezas más pequeñas o reemplazarlos por algoritmos y estructuras de datos libres de lockings donde sea posible para maximizar el rendimiento.

Con respecto a la segunda pregunta, no todos los lenguajes de scripting usan esto, pero solo los hace menos poderosos. Por ejemplo, los hilos en Ruby son verdes y no nativos.

En Python, los hilos son nativos y GIL solo evita que se ejecuten en diferentes núcleos.

En Perl, los hilos son aún peores. Simplemente copian todo el intérprete y están lejos de ser tan utilizables como en Python.

Tal vez este artículo del BDFL ayude.