PySide/Cython 和 GIL 多线程使用

Posted

技术标签:

【中文标题】PySide/Cython 和 GIL 多线程使用【英文标题】:PySide/Cython and GIL multi-thread use 【发布时间】:2013-01-28 10:53:04 【问题描述】:

我已经为 PySide/Cython 多线程应用程序苦苦挣扎了好几天。 将问题拆分为多个部分,我进行了一项使用 valgrind/helgrind 运行的测试(请参阅下面的一个错误条目)。 C 函数(下面的 CHLone load)使用 Python 及其 GIL(对 Cython 没有指令), Shiboken 也可以访问 GIL。 输出表明与 GIL 访问存在冲突(除非我错了),但我的 理解是 GIL 的存在是为了避免这种冲突。

Shiboken 执行一些与另一个线程上的PyList_New 冲突的dealloc...

我迷路了, 我认为 GIL 负责阻塞 PyList_New,而另一个线程正在修改一些 Python 共享数据。还是 Shiboken 忘记锁了?

listobject.c 第 161 行是对 _PyObject_GC_TRACK() 的调用(我假设)要求 GC 跟踪新对象。classobject.c 第 2360 行是对 _PyObject_GC_UNTRACK() 的调用,看起来很像要求 GC 停止跟踪对象... 我看到 helgrind 诊断是可能的数据竞争,但事实上这会导致核心转储,我不喜欢这种情况可能 > 使用线程时有关 Python GC 的词。我想先解决这个问题。

==26535== ----------------------------------------------------------------
==26535== 
==26535== Possible data race during read of size 8 at 0x4FE8488 by thread #2
==26535== Locks held: none
==26535==    at 0x4C92A80: PyList_New (listobject.c:161)
==26535==    by 0x742C43F: s2p_parseAndReadHDF (SIDStoPython.c:949)
==26535==    by 0x742C5C4: s2p_parseAndReadHDF (SIDStoPython.c:968)
==26535==    by 0x742E638: s2p_loadAsHDF (SIDStoPython.c:1485)
==26535==    by 0x741C3CC: __pyx_f_6CHLone_load (pyCHLone.c:2182)
==26535==    by 0x741D2AD: __pyx_pf_6CHLone_12load (pyCHLone.c:2422)
==26535==    by 0x741D1C3: __pyx_pw_6CHLone_13load (pyCHLone.c:2392)
==26535==    by 0x4D0A48F: PyEval_EvalFrameEx (ceval.c:4013)
==26535==    by 0x4D0C3DC: PyEval_EvalCodeEx (ceval.c:3253)
==26535==    by 0x4C8B641: function_call (funcobject.c:526)
==26535==    by 0x4C5F652: PyObject_Call (abstract.c:2529)
==26535==    by 0x4C7279E: instancemethod_call (classobject.c:2578)
==26535== 
==26535== This conflicts with a previous write of size 8 by thread #1
==26535== Locks held: none
==26535==    at 0x4C6C53F: instancemethod_dealloc (classobject.c:2360)
==26535==    by 0x5AB248A: Shiboken::AutoDecRef::~AutoDecRef() (in /tmp/tools-2/local/x86z/lib/python2.7/site-packages/PySide/QtCore.so)
==26535==    by 0x5F9736F: PySide::GlobalReceiverV2::qt_metacall(QMetaObject::Call, int, void**) (in /tmp/tools-2/local/x86z/lib/libpyside-python2.7.so.1.0.9)
==26535==    by 0x659BCA5: QObject::event(QEvent*) (in /tmp/tools-2/local/x86z/lib/libQtCore.so.4.8.0)
==26535==    by 0x5B038C5: QCoreApplicationWrapper::notify(QObject*, QEvent*) (in /tmp/tools-2/local/x86z/lib/python2.7/site-packages/PySide/QtCore.so)
==26535==    by 0x6586F8B: QCoreApplication::notifyInternal(QObject*, QEvent*) (in /tmp/tools-2/local/x86z/lib/libQtCore.so.4.8.0)
==26535==    by 0x658A5A7: QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) (in /tmp/tools-2/local/x86z/lib/libQtCore.so.4.8.0)
==26535==    by 0x65B60F2: ??? (in /tmp/tools-2/local/x86z/lib/libQtCore.so.4.8.0)
==26535==

问题: 1. 我对 GIL 的假设是否正确? 2. 如果, ... 是否意味着我必须自己管理每个线程中的 GIL 锁? ...然后我必须为 Python 创建自己的 mutex?!??!? 3. 如果, ... 关于 GIL 的使用,我的 Cython 或 C 库中是否存在问题? ... 会不会是 QThread/QMutex 的副作用?

【问题讨论】:

【参考方案1】:

在 CPython 中,全局解释器锁或 GIL 是一个互斥锁,可防止多个本机线程同时执行 Python 字节码。

因此,您可以从 GIL 中得到的所有期望是,它可以防止两个 Python 代码流同时执行。除此之外,您不应期望任何类型的锁定行为。编写依赖于保护您的 GIL 的代码通常是不好的做法。 GIL 是许多人想要移除的疣,它在某些形式的 python 中不存在,例如 IronPython 和 jython。在它确实存在的地方,它会降低并发性、增加开销并使 Python 代码比其他情况下更难推理。

从上面我不清楚是否涉及任何 GIL 故障。第二个痕迹似乎在 C++ 领域的深处,而不是在我理解 GIL 限制的领域。

【讨论】:

以上是关于PySide/Cython 和 GIL 多线程使用的主要内容,如果未能解决你的问题,请参考以下文章

python3.7多线程代码不执行?

Python 多线程 多进程 GIL

Python 多线程 多进程 GIL

27 Apr 18 GIL 多进程多线程使用场景 线程互斥锁与GIL对比 基于多线程实现并发的套接字通信 进程池与线程池 同步异步阻塞非阻塞

python3多线程和GIL全局解释器所

python多线程多进程以及GIL