2

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

我正在用 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++ 库和connection 试图通知所有的观察者,此时就是这里的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 为每次与 python 的交互处理 GIL 本身?我在代码中找不到任何东西,而且很难找到 source.send 调用进入 boost_python 的位置:(

4

3 回答 3

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

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

于 2010-01-22T17:37:43.340 回答
4

我在邮件列表上发现了一个非常晦涩的帖子,说要使用 PyEval_InitThreads(); 在 BOOST_PYTHON_MODULE 中,这实际上似乎停止了崩溃。

无论程序是否报告它收到的所有消息,它仍然是一个废话。如果我发送 2000,大多数时候它会说它收到了 2000,但有时它报告的数量要少得多。

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

修复只是做

BOOST_PYTHON_MODULE(MyLib)
{
    PyEval_InitThreads();
    class_ stuff
于 2009-12-20T07:03:23.457 回答
2

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

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

于 2010-08-13T08:34:06.463 回答