2009-03-16 8 views
18

Uno de los puntos más fuertes de Python es la facilidad de escribir extensiones C y C++ para acelerar las partes del código que consumen gran cantidad de procesadores. ¿Pueden estas extensiones evitar el bloqueo de intérprete global o también están restringidas por el GIL? Si no es así, esta "facilidad de extensión" es una característica aún más radical de lo que creía. Sospecho que la respuesta no es un simple sí o no, pero no estoy seguro, así que estoy haciendo la pregunta aquí en StackOverflow.Concurrencia: ¿Las extensiones de Python escritas en C/C++ están afectadas por el bloqueo de intérprete global?

Respuesta

7

Las extensiones de C/C++ a Python no están sujetas a GIL. Sin embargo, realmente necesitas saber lo que estás haciendo. From http://docs.python.org/c-api/init.html:

El bloqueo de intérprete global se utiliza para proteger el puntero al estado del hilo actual. Al soltar el bloqueo y guardar el estado del hilo, el puntero de estado del hilo actual debe recuperarse antes de liberar el bloqueo (ya que otro hilo podría adquirir el bloqueo inmediatamente y almacenar su propio estado de hilo en la variable global). Por el contrario, al adquirir el bloqueo y restablecer el estado del hilo, el bloqueo debe adquirirse antes de almacenar el puntero de estado del hilo.

¿Por qué sigo con tanto detalle al respecto? Porque cuando los subprocesos se crean desde C, no tienen el bloqueo de intérprete global, ni existe una estructura de datos de estado de subproceso para ellos. Dichos subprocesos deben iniciarse automáticamente, primero creando una estructura de datos de estado de subproceso, luego adquiriendo el bloqueo y finalmente almacenando su puntero de estado de subproceso, antes de que puedan comenzar a usar la API de Python/C. Cuando terminen, deben reiniciar el puntero de estado de la secuencia, liberar el bloqueo y finalmente liberar su estructura de datos de estado de la secuencia.

+2

¿Está pensando, quizás, en el código C que * integra * extensiones de Python, en lugar de C?Cuando un hilo en el intérprete de Python llama a su extensión C, todavía retiene el GIL a menos que lo libere específicamente. La sección que citó habla de hilos creados * fuera de * Python en total. –

+0

Supongo que el OP es ambiguo. Puede girar un hilo en una extensión C que no está vinculada por el GIL. –

16

Sí, las llamadas a las extensiones C (rutinas C llamadas desde Python) todavía están sujetas a GIL.

Sin embargo, puede manualmente liberar el GIL dentro de su extensión C, siempre y cuando tenga cuidado de volver a afirmarlo antes de devolver el control a la máquina virtual de Python.

Para obtener más información, echar un vistazo a las Py_BEGIN_ALLOW_THREADS y Py_END_ALLOW_THREADS macros: http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock

0

Salida Cython, tiene una sintaxis similar a Python pero con algunas construcciones como "cdef", funciones de acceso numpy rápidas, y una "con nogil" declaración (que hace lo que dice).

0

Si está escribiendo su extensión en C++, puede usar RAII para escribir fácil y legiblemente código manipulando el GIL. Yo uso este par de structlets RAII:

namespace py { 

    namespace gil { 

     struct release { 
      PyThreadState* state; 
      bool active; 

      release() 
       :state(PyEval_SaveThread()), active(true) 
       {} 

      ~release() { if (active) { restore(); } } 

      void restore() { 
       PyEval_RestoreThread(state); 
       active = false; 
      } 
     }; 

     struct ensure { 
      PyGILState_STATE* state; 
      bool active; 

      ensure() 
       :state(PyGILState_Ensure()), active(true) 
       {} 

      ~ensure() { if (active) { restore(); } } 

      void restore() { 
       PyGILState_Release(state); 
       active = false; 
      } 
     }; 

    } 

} 

... permitiendo que el GIL a ser activado por un bloque dado (de forma semántica que puede parecer vagamente familiar para los fans contexto gerente Pythonista):

PyObject* YourPythonExtensionFunction(PyObject* self, PyObject* args) { 

    Py_SomeCAPICall(…);  /// generally, if it starts with Py* it needs the GIL 
    Py_SomeOtherCall(…); /// ... there are exceptions, see the docs 

    { 
     py::gil::release nogil; 
     std::cout << "Faster and less block-y I/O" << std::endl 
        << "can run inside this block -" << std::endl 
        << "unimpeded by the GIL"; 
    } 

    Py_EvenMoreAPICallsForWhichTheGILMustBeInPlace(…); 

} 

... De hecho, personalmente también me resulta fácil extender Python, y el nivel de control que se tiene sobre las estructuras internas y el estado, una característica clave.

+0

N.B. Si está utilizando cualquiera de estas llamadas API, primero querrá realizar una llamada inicial a 'PyEval_InitThreads()' (como, por ejemplo, en la función de inicialización de su módulo) antes de intentar cualquier control GIL manual. – fish2000

Cuestiones relacionadas