2011-11-04 10 views
8

Próbuję owinąć kawałek kodu C++ do pytona lib przy użyciu boost.python, jednak okazało się, że wiele wystąpień nie może działać w tym samym czasie:boost.python nie wspiera równoległości?

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

} 

który jest opracować używając cUpewnij (cUpewnij)

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

i zbadano za pomocą następującego kodu (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() 

Dane wyjściowe wskazują, że kod jest zrównoleglony w bardzo dziwny sposób. Każdy wątek powinien podjąć tylko 2 sekundy i 4 wątki powinny działać jednocześnie na moim komputerze 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 

dziękuję za pomoc!

+9

Dobrze , to na pewno wygląda jak legalna aplikacja ...;) – larsmoa

+0

Byłoby łatwiej przeczytać, jeśli wskazałeś, który kod był python i który był w C++. Rozgryzłem to, ale zajęło mi to chwilę. –

Odpowiedz

16

To, na co się natknąłeś, to globalna blokada interpretera pythona. GIL pozwala na uruchamianie tylko jednego wątku w interpreterze pythona.

Jedną z zalet Boost.Python jest to, że możesz wydać GIL, zrobić C++, a następnie zabrać go z powrotem, gdy skończysz. Jest to jednak również odpowiedzialność. Python normalnie uwalnia GIL w regularnych odstępach czasu, aby dać innym wątkom szansę na uruchomienie. Jeśli jesteś w C++, to jest twoja praca. Jeśli przejdziecie Państwo przez 2 godziny chrupania przez trzymanie GIL-a, zamrozimy całego tłumacza.

To może być łatwo naprawić przy odrobinie odwrotnej RAII:

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

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

Teraz możesz zmienić swój kod tak:

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

bardzo ważne jest, aby pamiętać, że MUSISZ NIE dotykać dowolny kod Pythona lub dane Pythona lub wywołać interpreter, nie trzymając GIL. Spowoduje to awarię tłumacza.

Możliwe jest również przejście w drugą stronę. Wątek, w którym aktualnie nie znajduje się GIL, może go uzyskać i wykonywać połączenia do pythona. Może to być wątek, który wydał GIL wcześniej, lub który rozpoczął się w C++ i nigdy nie miał GIL. Oto klasa RAII:

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

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

Użycie jest pozostawione jako ćwiczenie dla ucznia.

Dodatkowa nota (ja zawsze zapomnieć o tym wspomnieć):

Jeśli będą pałować z GIL w C++ twoja definicja moduł musi zacząć z tym kodem:

BOOST_PYTHON_MODULE(ModuleName) 
{ 
    PyEval_InitThreads(); 

    ... 
} 
+0

Wielkie dzięki! To rozwiązało problem! – guinny

+0

Tak, ważne, aby zwolnić GIL, jeśli masz wywoływania kodu Pythona do C++, ponieważ wszelkie inne wątki, które mogą wymagać wywołania Pythona nie działają inaczej. –

+0

@fireant: dzięki za naprawienie mojego kodu. –

Powiązane problemy