使用GIL和多线程处理嵌入式Python解释器调用



星座/上下文

一种动态链接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;
}

最新更新