C++ 应用中的 Python C API - 何时锁定
我正在尝试写一个C++类,这个类会调用一个Python类的方法,这个方法会进行一些输入输出操作,比如文件操作和输出到屏幕。现在我遇到的问题是,我的这个类会被不同的线程调用:有时候是主线程,有时候是其他线程。显然,我尝试过在多线程的本地应用中调用Python的方法。基本上,一切都从PyEval_AcquireLock和PyEval_ReleaseLock开始,或者使用全局锁。根据文档的说明,当一个线程已经被锁住时,就会出现死锁的情况。当我的类从主线程或其他阻塞Python执行的线程被调用时,就会发生死锁。
在Python中调用Cfunc1() - 这是一个C++函数,它内部创建线程,这些线程会调用“我的类”。这个过程在PyEval_AcquireLock时卡住了,显然Python已经被锁住了,也就是说,它在等待C++的Cfunc1调用完成……如果我不使用这些锁,它就能正常完成。而且当Python解释器准备好接收下一个用户命令时,它也能正常完成,也就是说,当线程在后台调用函数时 - 而不是在本地调用内部。
我在寻找一个解决办法。我需要区分全局锁是否被允许,也就是说,Python没有被锁住并且准备好接收下一个命令……我尝试过PyGIL_Ensure,但不幸的是,我看到程序挂起了。
有没有什么已知的API或解决方案可以解决这个问题?
(Python 2.4)
1 个回答
除非你以非常特殊的方式包装了你的C++代码,否则当任何Python线程调用你的C++代码时,全局解释器锁(GIL)会被占用。如果你在C++代码中想做一些不需要与Python交互的耗时任务,你可以释放这个锁,然后在需要与Python交互时再重新获取它。具体可以参考文档:如果你只是使用传统的C API,里面有一些宏可以帮助你,推荐的用法是:
Py_BEGIN_ALLOW_THREADS
...Do some blocking I/O operation...
Py_END_ALLOW_THREADS
文档中解释说:
Py_BEGIN_ALLOW_THREADS这个宏会开启一个新的代码块,并声明一个隐藏的局部变量;而Py_END_ALLOW_THREADS这个宏则会关闭这个代码块。使用这两个宏的另一个好处是,当Python在没有线程支持的情况下编译时,它们会被定义为空,这样就省去了线程状态和GIL的操作。
所以,直到你明确释放了GIL(最好是用那个宏),并且需要再次与Python交互之前,你就不需要去获取GIL(而且也不应该去获取)。(文档中提到的“某些阻塞的I/O操作”,实际上可以是任何没有与Python交互的长时间运行的操作)。