- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在用 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/
我正在用 C++ 编写一个 Python 扩展,包装一个我不控制的第三方库。该库创建了一个 Python 一无所知的线程,并从该线程调用我提供给该库的 C++ 回调。我希望该回调调用 Python 函
我有一个使用 Python 库的 Windows 例程。我从 3.6.5 和一个静态库开始。得到它的工作。移至 .dll Python 库。也有效。移动到 3.7。不起作用。当我尝试通过调用 PyGI
我是一名优秀的程序员,十分优秀!