Python,Threads,GIL和C ++

是否有一些方法可以使boost :: python控制Python GIL与python的每次交互?

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

外部库是用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和一些非常好的function来锁定事物。

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

 struct IConnectionObserver_wrapper : Observers::IConnectionObserver, wrapper { 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的位置:(

我在邮件列表上发现了一个非常模糊的post,据说使用了PyEval_InitThreads(); 在BOOST_PYTHON_MODULE中,实际上似乎停止了崩溃。

它仍然是一个垃圾射击是否该程序报告它得到的所有消息。 如果我发送2000,大多数时候它说2000年,但有时它报告显着更少。

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

要解决这个问题

 BOOST_PYTHON_MODULE(MyLib) { PyEval_InitThreads(); class_ stuff 

不完全了解您的问题,但请看看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<>() ); 

调用策略可以帮助您更轻松地编写此代码。

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

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

Interesting Posts