2012-09-12 24 views
12

I tiene algunas funciones que interactivamente cargar módulos Python usando __import__de importación dentro de un hilo de Python

recientemente me topé con algún artículo sobre un "bloqueo de importación" en Python, es decir, una cerradura específicamente para las importaciones (no sólo el GIL). Pero el artículo era viejo, así que tal vez ya no sea así.

Esto me hace preguntarme sobre la práctica de importar en un hilo.

  1. Son import/__import__ thread safe?
  2. ¿Pueden crear bloqueos muertos?
  3. ¿Pueden causar problemas de rendimiento en una aplicación con subprocesos?

EDITAR 12 de septiembre de 2012

Gracias por la gran respuesta Soravux. Así que la importación es segura para subprocesos, y no me preocupo por los interbloqueos, ya que las funciones que usan __import__ en mi código no se llaman entre sí.

¿Sabe si el bloqueo se adquiere incluso si el módulo ya se ha importado? Si ese es el caso, probablemente debería buscar en sys.modules para verificar si el módulo ya ha sido importado antes de hacer una llamada al __import__.

Claro, esto no debería marcar una gran diferencia en CPython ya que de todos modos hay GIL. Sin embargo, podría hacer una gran diferencia en otras implementaciones como Jython o python sin apilamiento.

EDITAR 19 de Sept 2012

Sobre Jython, aquí está lo que se dice en el documento:

http://www.jython.org/jythonbook/en/1.0/Concurrency.html#module-import-lock

Python hace, sin embargo, definir un bloqueo de importación de módulo, que es implementado por Jython. Este bloqueo se adquiere cada vez que se realiza una importación de cualquier nombre . Esto es así independientemente de si la importación pasa por la instrucción import , el equivalente __import__ incorporado o código relacionado. Es importante tener en cuenta que incluso si el módulo correspondiente ya se ha importado , el bloqueo de importación del módulo seguirá adquiriéndose, si solo brevemente.

Por lo tanto, parece que tendría sentido comprobar sys.modules antes de realizar una importación, para evitar adquirir el bloqueo. ¿Qué piensas?

+1

Imagino que son seguros para subprocesos, ya que como dices, el intérprete bloquea las importaciones. Me interesaría averiguar si podrían causar interbloqueos de una forma distinta a las importaciones circulares típicas sin rosca. –

Respuesta

8

Las importaciones normales son seguras para hilos porque adquieren un bloqueo de importación antes de la ejecución y lo liberan una vez que se realiza la importación. Si agrega sus propias importaciones personalizadas utilizando los ganchos disponibles, asegúrese de agregarle este esquema de bloqueo. Se puede acceder a las facilidades de bloqueo en Python mediante el módulo imp (imp.lock_held()/acquire_lock()/release_lock()).

El uso de este bloqueo de importación no creará ningún punto muerto ni errores de dependencia aparte de las dependencias circulares que son already known.

Llamada de bajo nivel para crear un hilo siendo clone en Linux, enhebrar en Python es entonces una operación similar a un tenedor. Bifurcación y clonación aplica diferentes comportamientos en los diversos segmentos de memoria. Por ejemplo, solo la pila no es compartida por subprocesos, en comparación con las horquillas que clonan más segmentos (Datos (a menudo COW), Pila, Código, Heap), efectivamente no comparten su contenido. El mecanismo de importación en Python utiliza el espacio de nombres global que es no colocado en la pila, utilizando así un segmento compartido con sus hilos. Dado que los efectos secundarios (es decir, los cambios en la memoria) de la importación funcionan en los mismos segmentos, se comporta como un programa de subproceso único. Sin embargo, tenga cuidado de usar librerías de seguridad de subprocesos en sus importaciones en programas multiproceso. Es causar caos para utilizar llamadas a funciones que no son seguros para subprocesos en dicho entorno. Por cierto, los programas con subprocesos en Python sufren el GIL que no permitirá obtener muchas ganancias de rendimiento a menos que su programa esté vinculado a E/S o dependa de C o bibliotecas externas libres de subprocesos (ya que liberan el GIL antes de la ejecución) . Al ejecutar en dos subprocesos, la misma función importada no se ejecutará simultáneamente debido a este GIL. Tenga en cuenta que esto es solo una limitación de CPython y que otras implementaciones de Python tendrán un comportamiento diferente.

Para responder a su edición, Python tiene todos los módulos importados en caché. Si el módulo ya está cargado en la caché, no se ejecutará de nuevo y la declaración de importación (o función) volverá de inmediato. No tiene que implementarse usted mismo la búsqueda de caché en sys.modules, Python lo hace por usted y no imp bloqueará nada, aparte del GIL para la búsqueda de sys.modules.

Para responder a su segunda edición: prefiero tener que mantener un código más simple que tratando de optimizar las llamadas a las bibliotecas que uso (en este caso, la biblioteca estándar). El razonamiento es que el tiempo requerido para realizar algo suele ser mucho más importante que el tiempo requerido para importar el módulo que lo hace. Además, el tiempo requerido para mantener este tipo de código durante todo el proyecto es mucho mayor que el tiempo que llevará ejecutarlo. Todo se reduce a: "el tiempo del programador es más valioso que el tiempo de CPU".

+0

Gracias por la gran respuesta. Ahora me pregunto si el bloqueo de importación se adquiere incluso cuando un módulo ha sido importado previamente (ver edición en mi pregunta original). –

+0

Respuesta editada para reflejar su pregunta editada. Buen punto sobre otros sabores de Python. – Soravux

+1

Me tropecé con esta parte de la documentación: http://docs.python.org/library/threading.html#importing-in-threaded-code, que menciona algunos aspectos más. – Alfe

Cuestiones relacionadas