gpt4 book ai didi

python - PyGILState_Ensure() 导致死锁

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:33:25 26 4
gpt4 key购买 nike

我正在用 C++ 编写一个 Python 扩展,包装一个我不控制的第三方库。该库创建了一个 Python 一无所知的线程,并从该线程调用我提供给该库的 C++ 回调。我希望该回调调用 Python 函数,但我使用从文档中读取的方法遇到了死锁。这是我对这些的解释。

void Wrapper::myCallback()
{
PyGILState_STATE gstate=PyGILState_Ensure();
PyObject *result=PyObject_CallMethod(_pyObj,"callback",nullptr);
if (result) Py_DECREF(result);
PyGILState_Release(gstate);
}

我的代码没有做任何与线程相关的事情,尽管我已经尝试了很多其他事情。基于this ,例如,我尝试调用 PyEval_InitThreads(),但不清楚应该在何处进行扩展调用。我把它放在 PyMODINIT_FUNC 中。这些尝试都导致死锁、崩溃或来自 Python 的神秘 fatal error ,例如,PyEval_ReleaseThread:错误的线程状态

这是在带有 Python 3.6.1 的 Linux 上。有什么想法可以让这个“简单”的回调起作用吗?

可能的罪魁祸首

我没有意识到在另一个线程中,库处于忙/等待循环中等待回调的线程。在 gdb 中,info threads 使这一点显而易见。我能看到的唯一解决方案是跳过那些特定的回调调用;考虑到繁忙/等待循环,我看不到让它们安全的方法。在这种情况下,这是可以接受的,这样做可以消除死锁。

此外,看来我确实还需要在执行任何这些操作之前调用 PyEval_InitThreads()。在 C++ 扩展中,不清楚应该去哪里。其中一个回复建议在 Python 中通过创建和删除一次性 threading.Thread 来间接执行此操作。这似乎并没有解决它,而是触发了一个致命的 Python 错误:take_gil: NULL tstate,我认为这意味着仍然没有 GIL。我的猜测,基于 this它所指的问题是 PyEval_InitThreads() 导致当前线程成为 GIL 的主线程。如果该调用是在短暂的一次性线程中进行的,那么这可能是一个问题。是的,我只是在猜测,非常感谢不需要这样做的人的解释。

最佳答案

此答案仅适用于 Python >= 3.0.0。我不知道它是否适用于早期的 Python。

将你的 C++ 模块包装在一个看起来像这样的 Python 模块中:

import threading
t = threading.Thread(target=lambda: None, daemon=True)
t.run()
del t
from your_cpp_module import *

根据我对文档的阅读,这应该强制在导入模块之前初始化线程。然后你在那里写的回调函数应该可以工作。

我不太相信这个工作,但你的模块初始化函数可以改为这样做:

if (!PyEval_ThreadsInitialized())
{
PyEval_InitThreads();
}

这应该有效,因为如果 PyEval_ThreadsInitialized() 不正确,那么您的模块初始化函数应该由存在的唯一 Python 线程执行,此时持有 GIL 是正确的做法。

这些都是我的猜测。我从来没有做过这样的事情,我对你的问题的无能评论证明了这一点。但根据我对文档的阅读,这两种方法都应该有效。

关于python - PyGILState_Ensure() 导致死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47167251/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com