Python,Threads,GIL和C ++

时间:2009-12-20 04:56:05

标签: c++ python c multithreading boost-python

有没有办法让boost :: python控制Python GIL与python的每次交互?

我正在用boost :: python编写一个项目。我正在尝试为外部库编写C ++包装器,并使用python脚本控制C ++库。我无法更改外部库,只能更改我的包装程序。 (我正在为所述外部库编写功能测试应用程序)

外部库是用C语言编写的,它使用函数指针和回调来做很多繁重的工作。它是一个消息传递系统,因此当消息进入时,会调用回调函数,例如。

我在我的库中实现了一个观察者模式,以便多个对象可以监听一个回调。我已经把所有主要玩家都妥善出口了,我可以很好地控制到某一点。

外部库创建线程来处理消息,发送消息,处理等等。其中一些回调可能是从不同的进程调用的,我最近发现python不是线程安全的。

这些观察者可以在python中定义,所以我需要能够调用python和python需要在任何时候调用我的程序。

我像这样设置对象和观察者

class TestObserver( MyLib.ConnectionObserver ):
    def receivedMsg( self, msg ):
        print("Received a message!")

ob = TestObserver()
cnx = MyLib.Conection()
cnx.attachObserver( ob )

然后我创建一个发送到连接的源,并调用receivedMsg函数。

所以常规的source.send('msg')将进入我的C ++应用程序,转到C库,它将发送消息,连接将获取它,然后调用回调,这回到我的C ++库和连接尝试通知所有观察者,此时这是python类,所以它调用该方法。

当然,回调是从连接线程调用的,而不是主应用程序线程。

昨天一切都崩溃了,我无法发送1条消息。然后在Cplusplus-sig档案中挖掘后,我了解了GIL和几个非常好的功能来锁定事物。

所以我的观察者类的C ++ python包装器现在看起来像这样

struct IConnectionObserver_wrapper : Observers::IConnectionObserver, wrapper<Observers::IConnectionObserver>
{
    void receivedMsg( const Message* msg )
    {
        PyGILState_STATE gstate = PyGILState_Ensure();
        if( override receivedMsg_func = this->get_override( "receivedMsg" ) )
            receivedMsg_func( msg );
        Observers::IConnectionObserver::receivedMsg( msg );
        PyGILState_Release( gstate );
    }
}

然而,当我尝试发送超过250条消息时,这就是工作

for i in range(250)
    source.send('msg")
它又崩溃了。使用与之前相同的消息和症状,

PyThreadState_Get: no current thread

所以我想这次调用我的C ++应用程序时遇到问题,而不是调用python。

我的问题是,是否有一些方法可以使boost :: python处理GIL本身与python的每次交互?我在代码中找不到任何东西,并且很难找到source.send调用进入boost_python的位置:(

3 个答案:

答案 0 :(得分:4)

我在邮件列表上发现了一个非常模糊的帖子 PyEval_InitThreads(); 在BOOST_PYTHON_MODULE中 这实际上似乎可以阻止崩溃。

它仍然是一个垃圾拍摄,无论程序是否报告它获得的所有消息。如果我发送2000,大多数时候它说它有2000,但有时报告显着减少。

我怀疑这可能是因为线程同时访问我的计数器,所以我回答这个问题,因为这是一个不同的问题。

要解决这个问题

BOOST_PYTHON_MODULE(MyLib)
{
    PyEval_InitThreads();
    class_ stuff

答案 1 :(得分:4)

完全不了解您的问题,但请查看CallPolicies:

http://www.boost.org/doc/libs/1_37_0/libs/python/doc/v2/CallPolicies.html#CallPolicies-concept

您可以定义新的调用策略(例如,一个调用策略是“return_internal_reference”),它将在执行包装的C ++函数之前和/或之后执行某些代码。我已成功实现一个调用策略,在执行C ++包装函数之前自动释放GIL并在返回Python之前再次获取它,因此我可以编写如下代码:

.def( "long_operation", &long_operation, release_gil<>() );

通话政策可以帮助您更轻松地编写此代码。

答案 2 :(得分:1)

我认为最好的方法是避免使用GIL并确保您与python的交互是单线程的。

我正在设计一个基于boost.python的测试工具,并且我认为我可能会使用生产者/消费者队列从多线程库调度事件,这些事件将由python线程按顺序读取。