我有一个 C++ 类
Runner
,它封装了运行本机线程。该类通过 pybind11
暴露给 Python,因此从 Python 中调用 start_thread
。
class Runner {
public:
void start_thread() {
thread_ = std::jthread([]{
try {
while (true) {
// do something
}
} catch (...) {
pybind11::gil_scoped_acquire gil;
pybind11::set_error(PyExc_RuntimeError, "Something horrible happend");
}
});
}
private:
std::jthread thread_;
};
本机线程中可能会抛出异常,有没有办法将异常从本机线程重新引发到Python?
我正在考虑使用 pybind11::set_error,但这是正确的方法吗?如果是这样,当 Python 意识到存在异常时,有什么保证呢?如果本机线程释放 Gil 后立即检查异常 - 那么这个解决方案应该没问题。
就像你注意到的,你不能让 C++ 异常逃逸
jthread
,因为它将在 C++ 端处理并导致崩溃。相对好的方法是将 std::exception_ptr
存储在 Runner
中,并提供额外的接口以将其重新抛出到调用方:
class Runner {
public:
void start_thread() {
thread_ = std::jthread([this] {
try {
throwSomething();
} catch (const std::exception &e) {
ePtr_ = std::current_exception();
}
});
}
void rethrow_if_needed() const {
if (ePtr_) {
std::rethrow_exception(ePtr_);
}
}
private:
void throwSomething() { throw std::runtime_error("crit failure"); }
std::jthread thread_;
std::exception_ptr ePtr_;
};
那么你的 python 代码可能如下所示:
runner = Runner()
runner.start_thread()
try:
time.sleep(1)
runner.rethrow_if_needed()
except Exception as e:
print("caught: ", e)
std::runtime_error
应自动翻译为 pythonic RuntimeError
。