python - 为什么在两个线程中释放和获取 GIL 会导致应用程序崩溃?
问题描述
我已经使用 C++ 开发了一个 Python 扩展。这个模块的唯一功能是这样的:
static PyObject *TestModule_API1(PyObject *self, PyObject *args)
{
PyThreadState *_save;
_save = PyEval_SaveThread();
try
{
DoComputation1();
PyEval_RestoreThread(_save);
}
catch (const std::exception & e)
{
PyObject * py_exception = PyErr_NewException((char*)"pyhms.error", nullptr, nullptr);
PyErr_SetString(py_exception, e.what());
PyEval_RestoreThread(_save);
return nullptr;
}
Py_RETURN_NONE;
}
每当我用两个 Python 线程调用此方法时,如果该DoComputation1()
方法抛出异常,应用程序就会崩溃。即使将整个 try 块放在 std::mutex 中(在块的开头锁定并在块的末尾解锁)也不能解决问题。这个问题的根本原因是什么,我应该如何解决它?
我正在使用 Visual Studio 2013 和 Python 2.7 在 Windows 10 上进行开发。
编辑 1:
如果我将PyEval_RestoreThread(_save);
行(在 catch 块中)带到 catch 块的开头,则不会发生崩溃。这是否意味着在 GIL 发布期间,我不应该调用任何 Python API?
编辑 2:
我还需要使用互斥锁保护我的 API1 方法免受并发线程的影响。我应该在释放 GIL 之前锁定我的互斥锁吗?有没有可能导致死锁的情况?
解决方案
这个问题的根本原因是什么,我应该如何解决它?
问题的根本原因是,如果你DoComputation1()
在两个线程中运行并且这个方法抛出异常,两个线程都会运行 catch 块。在 catch 块中,使用了一些 Python API 函数。因此,这意味着 Python 实现中确实存在两个线程,这将导致崩溃。
如果我带上 PyEval_RestoreThread(_save); 行(在 catch 块中)到 catch 块的开头,不会发生崩溃。这是否意味着在 GIL 发布期间,我不应该调用任何 Python API?
如果将PyEval_RestoreThread(_save);
行带到catch块的第一行,则表示catch块内的代码将由两个线程依次执行。因此,没有发生崩溃。
我应该在释放 GIL 之前锁定我的互斥锁吗?
我认为最好使用类似std::lock(...)
或类似的结构同时将它们锁定在一起。但为此,您首先需要 GIL 的包装类,使其成为可锁定对象。
有没有可能导致死锁的情况?
如果按照建议将它们(GIL 释放和互斥锁)放在一起,我认为不会发生死锁。
推荐阅读
- mysql - 您无权操纵用户
- python - 如何修复 ID 为“3/Ewurafua/processed.cleveland.data.csv”的 csv 上传不存在。也许它被删除了?
- android - 进度对话框在活动完成绘制 UI 之前关闭
- amazon-web-services - 为什么我的 AWS NLB 将流量转发到目标组中的单个 ec2 实例
- r - 如何计算 R data.table 中行子集的百分比?
- c# - 如何gridview将使用拖放更改行的索引
- python - 当我对文本数据进行聚类时,我应该使用什么 Vectorizer?
- c++ - 在跨平台桌面/移动应用程序套件中使用 ZeroMQ 解决架构问题
- javascript - 在某些 iOS 设备上使用触摸 yt iframe 阻止网页 java-scripts 滑动,如何解决?
- vba - 访问复选框值更新不会触发 AfterUpdate 事件