2011-11-04 11 views
8

estoy tratando de envolver un trozo de código C++ en Python lib utilizando Boost.Python, sin embargo, descubrí que varias instancias no se pueden ejecutar al mismo tiempo:¿boost.python no admite el paralelismo?

código (C++):

class Foo{ 
public: 
    Foo(){} 
    void run(){ 
     int seconds = 2; 
     clock_t endwait; 
     endwait = clock() + seconds * CLOCKS_PER_SEC ; 
     while (clock() < endwait) {} 
    } 

}; 

BOOST_PYTHON_MODULE(run_test) 
{ 
    using namespace boost::python; 

    class_<Foo>("test", init<>()) 
     .def("run", &Foo::run) 
     ; 

} 

que es compilar usando CMake (CMake):

add_library(run_test SHARED run_test.cpp) 
target_link_libraries(run_test boost_python python2.7) 

y probado con el siguiente código (Python):

class Dos(threading.Thread): 
    def run(self): 
     printl('performing DoS attack') 

     proc = test() 
     proc.run() 

for i in range(5): 
    t = Dos() 
    t.start() 

La salida indica que el código está paralelizado de una manera muy extraña. Cada hilo debe tener sólo 2 segundos y 4 hilos debe ejecutar de forma simultánea en mi máquina quadcore:

[2011-11-04 13:57:01] performing DoS attack 
[2011-11-04 13:57:01] performing DoS attack 
[2011-11-04 13:57:05] performing DoS attack 
[2011-11-04 13:57:05] performing DoS attack 
[2011-11-04 13:57:09] performing DoS attack 

gracias por su ayuda!

+9

Bueno , esto ciertamente parece una aplicación legítima ...;) – larsmoa

+0

Esto sería más fácil de leer si indicó qué código era python y cuál era C++. Lo descubrí, pero me tomó un momento. –

Respuesta

16

Lo que te encuentras es el bloqueo de Intérprete de Python Global. GIL solo permite ejecutar un hilo a la vez en el intérprete de Python.

Una de las ventajas de Boost.Python es que puede lanzar el GIL, hacer cosas en C++, y luego llevarlo de vuelta cuando haya terminado. Esto también es una responsabilidad sin embargo. Python normalmente libera el GIL a intervalos regulares, para dar la oportunidad a otros hilos de correr. Si estás en C++, este es tu trabajo. Si revisas números crujientes durante 2 horas mientras mantienes presionado el GIL, congelarás a todo el intérprete.

Esto puede ser fácil de solucionar con un poco de RAII inversa:

class releaseGIL{ 
public: 
    inline releaseGIL(){ 
     save_state = PyEval_SaveThread(); 
    } 

    inline ~releaseGIL(){ 
     PyEval_RestoreThread(save_state); 
    } 
private: 
    PyThreadState *save_state; 
}; 

Ahora puede cambiar el código de este modo:

class Foo{ 
public: 
    Foo(){} 
    void run(){ 
     { 
      releaseGIL unlock = releaseGIL(); 
      int seconds = 2; 
      clock_t endwait; 
      endwait = clock() + seconds * CLOCKS_PER_SEC ; 
      while (clock() < endwait) {} 
     } 
    } 
}; 

Es muy importante tener en cuenta que no debe tocar cualquier código python, o datos de pitón o llamar al intérprete sin tener el GIL. Esto hará que su intérprete se cuelgue.

También es posible ir por el otro camino. Un hilo que actualmente no tiene el GIL puede adquirirlo y realizar llamadas a python. Esto puede ser un hilo que lanzó el GIL anteriormente, o uno que comenzó en C++ y nunca tuvo el GIL. Aquí está la clase de RAII para eso:

class AcquireGIL 
{ 
public: 
    inline AcquireGIL(){ 
     state = PyGILState_Ensure(); 
    } 

    inline ~AcquireGIL(){ 
     PyGILState_Release(state); 
    } 
private: 
    PyGILState_STATE state; 
}; 

El uso se deja como un ejercicio para el estudiante.

Nota adicional (siempre me olvido de mencionar esto):

Si va a ser jugar con el GIL en C++ que su definición de módulo tiene que empezar con este código:

BOOST_PYTHON_MODULE(ModuleName) 
{ 
    PyEval_InitThreads(); 

    ... 
} 
+0

¡Muchas gracias! ¡Solucionó el problema! – guinny

+0

Sí, es importante que liberes el GIL si tienes un código python que llama a C++, ya que cualquier otro subproceso que pueda necesitar llamar a python no funcionará de lo contrario. –

+0

@fireant: gracias por arreglar mi código. –

Cuestiones relacionadas