2011-11-04 63 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:感谢您修复我的代码。 –