2011-11-04 38 views
8

我試圖一塊C++代碼包裝成蟒LIB使用Boost.Python的,但是,我發現了多個實例不能在同一時間運行:boost.python不支持並行性?

代碼(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) 
     ; 

} 

這是編譯使用CMake的(CMake的):

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

並用下面的代碼(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() 

輸出表明代碼以非常奇怪的方式並行化。每個線程應該只需要2秒鐘,4個線程應我的四核機器上同時運行:

[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 

感謝您的幫助!

+9

好,這當然看起來像一個合法的應用程序...;) – larsmoa

+0

如果您指出哪些代碼是python,哪些是C++,這將更容易閱讀。我知道了,但是我花了一點時間。 –

回答

16

你正在運行的是python Global Interpreter Lock。 GIL一次只允許一個線程在python解釋器中運行。

Boost.Python的優點之一是你可以釋放GIL,做C++的東西,然後當你完成後取回它。但這也是一項責任。 Python通常會定期釋放GIL,以使其他線程有機會運行。如果你使用C++,這是你的工作。如果您在持有GIL的同時持續2小時的緊縮數字,則會凍結整個解釋器。

這可以很容易一點點反向RAII解決:

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

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

現在你可以改變像這樣的代碼:

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

要注意,你們不要碰這是非常重要任何python代碼或python數據或在不保存GIL的情況下調用解釋器。這會導致你的解釋器崩潰。

也有可能走另一條路。當前沒有持有GIL的線程可以獲取它,並調用python。這可以是早些時候發佈GIL的線程,也可以是以C++開始並從未擁有GIL的線程。這裏是RAII類:

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

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

用法留給學生練習。

附加說明(我總是忘記提到這一點):

如果你將要在C++中的模塊定義需要開始使用此代碼與GIL搞亂:

BOOST_PYTHON_MODULE(ModuleName) 
{ 
    PyEval_InitThreads(); 

    ... 
} 
+0

非常感謝!它解決了問題! – guinny

+0

是的,如果你將Python代碼調用到C++中,那麼釋放GIL很重要,因爲任何其他可能需要調用python的線程都無法工作。 –

+0

@fireant:感謝您修復我的代碼。 –