星座/上下文:
一种动态链接C++共享库emb.so(2(的C++可执行文件(1(它又运行一个嵌入式python解释器(3(,该解释器调用自定义python函数(4(。
使用pybind11嵌入Python解释器(3(。从C++调用Python函数可以简化为:
py::module::import("test").attr("my_func")();
可执行文件(1(有一个主循环,它可以在其中做一些其他工作,但它会定期调用python函数。
观察:
- 变体1:如果我在python函数内部进行阻塞,python代码会顺利快速地执行,但主要的可执行循环显然被阻塞了
- 变体2:如果我在python函数内创建一个python线程以立即从函数返回,那么主可执行文件正在运行,但python代码运行速度非常慢(我可以逐个观察带有打印的for循环的迭代(
问题:
为什么变体2如此缓慢,我该如何修复它
我的猜测是这与GIL有关,在返回主循环之前,我试图在包装器emb.so中释放GIL,但如果没有segfault,我就无法做到这一点。
有什么想法吗?
事实证明,这与以下问题有很大关系:
在多线程C应用程序中嵌入python
(见答案https://stackoverflow.com/a/21365656/12490068)
我通过在调用如下嵌入Python代码后明确发布GIL来解决这个问题:
state = PyGILState_Ensure();
// Call Python/C API functions...
PyGILState_Release(state);
如果您在函数或其他C++作用域中执行此操作,并且您正在创建python对象,则必须确保在释放GIL后不会调用python对象的destructor。所以不要这样做:
void my_func() {
gil_state = PyGILState_Ensure();
py::int_ ret = pymodule->attr("GiveMeAnInt")();
PyGILState_Release(gil_state);
return ret.cast<int>();
}
而是做
void my_func() {
int ret_value;
gil_state = PyGILState_Ensure();
{
py::int_ ret = pymodule->attr("GiveMeAnInt")();
ret_value = ret.cast<int>();
}
PyGILState_Release(gil_state);
return ret_value;
}