为什么在两个线程内重新关联和获取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);线(在挡块中)直到接球块开始时,没有发生任何碰撞。这意味着在GIL发布期间,我不应该给任何人打电话Python API?

如果将PyEval_RestoreThread(_save);行放在catch块的第一行,则意味着catch块内的代码将由两个线程顺序执行。所以,没有发生崩溃。

我应该在释放的GIL之前锁定互斥锁吗?

我认为最好同时使用std::lock(...)之类的costruct来锁定它们。但为此,您首先需要一个GIL的包装器类,使其成为一个可锁定的对象。

是否存在可能导致死锁的情况?

如果按照建议将两者(GIL释放和互斥锁)放在一起,我认为不会发生死锁。

最新更新