2011-11-04 10 views
8

Sto cercando di avvolgere un pezzo di codice C++ in pitone lib utilizzando Boost.Python, però, ho scoperto che più istanze non possono funzionare allo stesso tempo:boost.python non supporta il parallelismo?

codice (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) 
     ; 

} 

che è compilare usando CMake (CMake):

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

e testato con il seguente codice (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() 

L'output indica che il codice è parallelizzato in un modo molto strano. Ogni thread dovrebbe richiedere solo 2 secondi e 4 thread dovrebbero essere eseguiti contemporaneamente sulla mia macchina 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 

grazie per il vostro aiuto!

+9

Bene , questo certamente sembra un'applicazione legittima ...;) – larsmoa

+0

Questo sarebbe più facile da leggere se indicassi quale codice fosse python e quale fosse C++. L'ho capito, ma mi ci è voluto un momento. –

risposta

16

Quello che stai incontrando è il Python Global Interpreter Lock. GIL consente solo l'esecuzione di un thread alla volta nell'interprete python.

Uno dei vantaggi di Boost.Python è che è possibile rilasciare GIL, eseguire attività in C++ e quindi riprenderlo quando si è terminato. Questa è anche una responsabilità comunque. Python rilascia normalmente GIL a intervalli regolari, per dare agli altri thread la possibilità di correre. Se sei in C++, questo è il tuo lavoro. Se si fanno i numeri di crunch per 2 ore mentre si tiene il GIL, si congelerà l'intero interprete.

Questo può essere facile da risolvere con un po 'RAII inverso:

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

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

Ora è possibile modificare il codice in questo 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) {} 
     } 
    } 
}; 

E' molto importante notare che non si deve toccare qualsiasi codice python o dati python o chiamate all'interprete mentre non si tiene il GIL. Ciò causerà l'arresto anomalo dell'interprete.

È anche possibile andare dall'altra parte. Un thread che al momento non contiene GIL può acquisirlo e fare chiamate a python. Questo può essere un thread che ha rilasciato GIL in precedenza, o uno che è iniziato in C++ e non ha mai avuto il GIL. Ecco la classe RAII per questo:

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

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

L'utilizzo è lasciato come esercizio per lo studente.

Nota aggiuntiva (mi dimentico sempre di citare questo):

Se avete intenzione di essere scherzi con il GIL in C++ tua definizione del modulo ha bisogno di iniziare con questo codice:

BOOST_PYTHON_MODULE(ModuleName) 
{ 
    PyEval_InitThreads(); 

    ... 
} 
+0

Grazie mille! Ha risolto il problema! – guinny

+0

Sì, importante che rilasci GIL se hai codice Python che chiama in C++, poiché qualsiasi altro thread che potrebbe dover chiamare in python non funzionerà diversamente. –

+0

@fireant: grazie per aver corretto il mio codice. –