【发布时间】:2017-08-03 15:56:55
【问题描述】:
我正在尝试在来自 boost::python 的 Python 解释器中运行并发 Python 脚本。这是一个简短的示例程序:
#include <Python.h>
#include <boost/python/exec.hpp>
#include <iostream>
#include <thread>
#include <boost/python/extract.hpp>
#include <boost/python/import.hpp>
#include <boost/python/object.hpp>
int main(int argc, char const *argv[]) {
const char *prog = "def ack(m, n):\n"
" if m == 0:\n"
" return n + 1\n"
" elif n == 0:\n"
" return ack(m - 1, 1)\n"
" else:\n"
" return ack(m - 1, ack(m, n - 1))";
Py_Initialize();
try {
std::thread t1([&prog]() {
std::cout << "t1" << std::endl;
boost::python::object mainModule = boost::python::import("__main__");
boost::python::object mainNamespace = mainModule.attr("__dict__");
boost::python::exec(prog, mainNamespace, mainNamespace);
int val = boost::python::extract<int>(
boost::python::eval("ack(3,3)", mainNamespace, mainNamespace));
std::cout << "t1 result: " << val << std::endl;
});
std::thread t2([&prog]() {
std::cout << "t2" << std::endl;
boost::python::object mainModule = boost::python::import("__main__");
boost::python::object mainNamespace = mainModule.attr("__dict__");
boost::python::exec(prog, mainNamespace, mainNamespace);
int val = boost::python::extract<int>(
boost::python::eval("ack(3,4)", mainNamespace, mainNamespace));
std::cout << "t2 result: " << val << std::endl;
});
t1.join();
t2.join();
} catch (boost::python::error_already_set const &e) {
PyErr_Print();
}
return 0;
}
问题是程序间歇性地失败。我已经在两个不同的 Linux 机器上试过了。一方面,它失败了大约 3/4 的时间;另一方面,大约有 1/10 的时间。最常见的失败消息是没有帮助的:
RUN FINISHED; Segmentation fault; core dumped; real time: 60ms; user: 0ms; system: 0ms
Boost Pythnon 文档中有几个关于并发的诱人调用,但我尝试过的组合都没有帮助。 global interpreter lock (GIL) 似乎被设计为允许 C++ 访问 same Python 解释器线程,除非我误解了它。我想要两个完全独立的 Python 解释器同时运行。
This example 很接近,但使用两个独立的 C 线程,偶尔调用 Python 来完成一些工作。我正在尝试同时运行两个单独的 Python 进程。
This question 类似,尽管它提供的细节较少。在我的例子中,Python 解释器用于将在超级计算机上运行需要相当长的时间的外部进程联系在一起。他们不需要回调我的 C++ 应用程序。完成后,C++ 应用程序会从外部拖放文件中收集和显示结果。
【问题讨论】:
-
Python 解释器不支持多线程并发执行。多个线程可以在同一个解释器中运行,但只能以协作的、分时的模式运行。本机线程可以并发运行,但它们对解释器和属于它的托管对象的访问必须通过 GIL 进行序列化。 Boost 无法改变这一点。
-
@JohnBollinger 似乎唯一的解决方案是在单独的进程而不是线程中执行并发 Python 工作,就像上面的示例代码试图做的那样。对吗?
-
是的,@ukhat,据我所知,在 Python 解释器本身中运行的代码的真正并发需要单独的进程。
标签: python c++ boost boost-python