如何从增强线程调用Python
How to call Python from a boost thread?
我有一个python应用程序,该应用称为c boost python库,这一切都可以。但是,我对Python方案有一个回调C ,其中Boost线程中的C 调用Python,并且在C 方面得到了访问违规。如果我使用Python线程进行完全相同的回调,则可以完美地工作。因此,我怀疑我不能使用Boost线程从C 回调Python,但需要做一些额外的工作才能工作?
最有可能的罪魁祸首是,在调用python代码时,全局解释器锁(GIL)不会由线程持有,从而导致不确定的行为。验证所有直接或间接python调用的路径,在调用Python代码之前获取GIL。
GIL是Cpython解释器周围的静音。该静音可防止在Python对象上执行并行操作。因此,在任何时间点,最大一个线程,即获得GIL的一个线程,可以在Python对象上执行操作。当存在多个线程时,调用Python代码,而不持有GIL会导致不确定的行为。
c或c 线程有时在Python文档中称为外星线程。Python解释器无法控制外星线程。因此,外星线程负责管理GIL,以允许使用Python线程并行执行。必须精心考虑:
- 堆栈放松,因为boost.python可能会抛出一个例外。
- python的间接电话,例如复制构建器或驱动器
一种解决方案是将python回调与知道吉尔管理的自定义类型。
使用RAII式类别管理GIL提供了优雅的异常安全解决方案。例如,使用以下with_gil
类,当创建with_gil
对象时,调用线程将获取GIL。当with_gil
对象破坏时,它会恢复GIL状态。
/// @brief Guard that will acquire the GIL upon construction, and
/// restore its state upon destruction.
class with_gil
{
public:
with_gil() { state_ = PyGILState_Ensure(); }
~with_gil() { PyGILState_Release(state_); }
with_gil(const with_gil&) = delete;
with_gil& operator=(const with_gil&) = delete;
private:
PyGILState_STATE state_;
};
及其用法:
{
with_gil gil; // Acquire GIL.
// perform Python calls, may throw
} // Restore GIL.
能够通过with_gil
管理GIL,下一步是创建一个正确管理GIL的函数。以下py_callable
类将包裹boost::python::object
并获取所有调用Python代码的路径的GIL:
/// @brief Helper type that will manage the GIL for a python callback.
///
/// @detail GIL management:
/// * Acquire the GIL when copying the `boost::python` object
/// * The newly constructed `python::object` will be managed
/// by a `shared_ptr`. Thus, it may be copied without owning
/// the GIL. However, a custom deleter will acquire the
/// GIL during deletion
/// * When `py_callable` is invoked (operator()), it will acquire
/// the GIL then delegate to the managed `python::object`
class py_callable
{
public:
/// @brief Constructor that assumes the caller has the GIL locked.
py_callable(const boost::python::object& object)
{
with_gil gil;
object_.reset(
// GIL locked, so it is safe to copy.
new boost::python::object{object},
// Use a custom deleter to hold GIL when the object is deleted.
[](boost::python::object* object)
{
with_gil gil;
delete object;
});
}
// Use default copy-constructor and assignment-operator.
py_callable(const py_callable&) = default;
py_callable& operator=(const py_callable&) = default;
template <typename ...Args>
void operator()(Args... args)
{
// Lock the GIL as the python object is going to be invoked.
with_gil gil;
(*object_)(std::forward<Args>(args)...);
}
private:
std::shared_ptr<boost::python::object> object_;
};
通过在自由空间上管理boost::python::object
,可以自由复制shared_ptr
而无需握住GIL。这使我们可以安全地使用默认生成的复制构建器,分配运算符,破坏者等。
一个人将使用py_callable
如下:
// thread 1
boost::python::object object = ...; // GIL must be held.
py_callable callback(object); // GIL no longer required.
work_queue.post(callback);
// thread 2
auto callback = work_queue.pop(); // GIL not required.
// Invoke the callback. If callback is `py_callable`, then it will
// acquire the GIL, invoke the wrapped `object`, then release the GIL.
callback(...);
这是一个完整的示例,证明让Python扩展名调用Python对象作为C 线程的回调:
#include <memory> // std::shared_ptr
#include <thread> // std::this_thread, std::thread
#include <utility> // std::forward
#include <boost/python.hpp>
/// @brief Guard that will acquire the GIL upon construction, and
/// restore its state upon destruction.
class with_gil
{
public:
with_gil() { state_ = PyGILState_Ensure(); }
~with_gil() { PyGILState_Release(state_); }
with_gil(const with_gil&) = delete;
with_gil& operator=(const with_gil&) = delete;
private:
PyGILState_STATE state_;
};
/// @brief Helper type that will manage the GIL for a python callback.
///
/// @detail GIL management:
/// * Acquire the GIL when copying the `boost::python` object
/// * The newly constructed `python::object` will be managed
/// by a `shared_ptr`. Thus, it may be copied without owning
/// the GIL. However, a custom deleter will acquire the
/// GIL during deletion
/// * When `py_callable` is invoked (operator()), it will acquire
/// the GIL then delegate to the managed `python::object`
class py_callable
{
public:
/// @brief Constructor that assumes the caller has the GIL locked.
py_callable(const boost::python::object& object)
{
with_gil gil;
object_.reset(
// GIL locked, so it is safe to copy.
new boost::python::object{object},
// Use a custom deleter to hold GIL when the object is deleted.
[](boost::python::object* object)
{
with_gil gil;
delete object;
});
}
// Use default copy-constructor and assignment-operator.
py_callable(const py_callable&) = default;
py_callable& operator=(const py_callable&) = default;
template <typename ...Args>
void operator()(Args... args)
{
// Lock the GIL as the python object is going to be invoked.
with_gil gil;
(*object_)(std::forward<Args>(args)...);
}
private:
std::shared_ptr<boost::python::object> object_;
};
BOOST_PYTHON_MODULE(example)
{
// Force the GIL to be created and initialized. The current caller will
// own the GIL.
PyEval_InitThreads();
namespace python = boost::python;
python::def("call_later",
+[](int delay, python::object object) {
// Create a thread that will invoke the callback.
std::thread thread(+[](int delay, py_callable callback) {
std::this_thread::sleep_for(std::chrono::seconds(delay));
callback("spam");
}, delay, py_callable{object});
// Detach from the thread, allowing caller to return.
thread.detach();
});
}
交互式用法:
>>> import time
>>> import example
>>> def shout(message):
... print message.upper()
...
>>> example.call_later(1, shout)
>>> print "sleeping"; time.sleep(3); print "done sleeping"
sleeping
SPAM
done sleeping
- 从C++调用 Python 并访问通过 Anaconda 安装的软件包
- 使用C++指针调用 python 函数
- 使用 pybind11 调用 Python 函数时避免复制输入数据
- 从C++调用 Python 脚本以持续流式传输数据
- 通过std::system调用python时出错
- 用c++调用python程序
- 在 C 代码中调用 Python 函数时第三次出现访问冲突写入位置错误
- 如何分发调用Python C++应用程序?
- 有没有办法用c ++ Gsoap客户端调用python soap spyne服务器?
- 使用来自C++的任意数量的参数(在编译时未知)调用 Python 函数
- 如何从 c++ 调用 python
- 从C++调用 Python 或 Lua 来计算表达式,仅在需要时计算未知变量
- 使用Tensorflow/Bazel从C++调用Python时出现链接错误
- 如何避免在Qt项目中调用Python脚本时出现分段错误
- 如何在C++环境中使用 Tensorflow 库调用 python 文件
- 从C++调用 Python 类方法,如果给定一个初始化的类作为 PyObject
- 使用 pybind11 从 C++ 调用 Python 函数
- 从C 中的多个线程调用Python函数
- numpy.core.multiarray在通过Xcode中开发的C 应用程序调用Python模块时未能导入
- 在QT Creator中使用C++调用Python脚本