PyQt 编程中多线程应该用 QThread、QTimer 还是 threading??

2018-01-31 13:20:01 +08:00
 XIVN1987

按我的理解,由于 GIL 的存在,threading.Thread 肯定无法利用多核,任意时刻只有一个线程在跑; QThread 的话在 C++里肯定是能多个同时跑的,但在 PyQt 里它执行的也是 Python 代码,所以应该也和 threading 一样并不能真的并行;而 QTimer 似乎原本就是在创建它的那个线程里跑的,但既然大家都没法真的并行,那跟另外两个似乎是一回事儿,,而且 QTimer 写起来感觉更简洁、直观,也不用 sleep(),,所以我觉得写 PyQt 程序的时候并发逻辑用 QTimer 写就行了,,

14042 次点击
所在节点    Python
32 条回复
crysislinux
2018-01-31 13:41:53 +08:00
qthread 是真线程。Python 只是包装,底下还是 c++在跑
XIVN1987
2018-01-31 13:46:29 +08:00
@crysislinux

可是 QThread 的 run 函数里的代码都是 python 代码啊,,比如:
class TimeThread(QtCore.QThread):
def run(self):
# ... ...
XIVN1987
2018-01-31 13:47:41 +08:00
v2ex 的回复真是,,,即不能修改、也不能预览,,
XIVN1987
2018-01-31 13:48:20 +08:00
@crysislinux
''' python
class TimeThread(QtCore.QThread):
def run(self):
# ... ...
'''
crysislinux
2018-01-31 13:49:31 +08:00
@XIVN1987 这个还真没仔细想过。但是我以前用过,你可以试试在线程里 sleep,看看主线程 ui 会卡住不
XIVN1987
2018-01-31 13:55:22 +08:00
@crysislinux

其实都不用试,要是真能通过这种方法实现真正的并行,,那大家就不会再提 GIL 的事情了,,
GeruzoniAnsasu
2018-01-31 13:55:39 +08:00
@XIVN1987 qt 的 runtime 都是 c++封装的,不必担心

类似于 run1(){PyEval_CallObject(run)}
XIVN1987
2018-01-31 13:58:13 +08:00
@GeruzoniAnsasu

只要你执行 Python 代码,,那 GIL 你就避不开,,

你这样一样得 Python 的虚拟机执行啊,,GIL 在虚拟机层面实现的。。
GeruzoniAnsasu
2018-01-31 14:09:51 +08:00
@XIVN1987 emmmmm 好像的确是这样,但属于 qt 框架本身的东西可以绕开 python 封装,worker 线程间可能会被 gil 影响,但 gui 的部分应该影响不着

当然了我也是猜的,确实没细想过这种问题
h4lbhg1G
2018-01-31 14:10:23 +08:00
Jython and IronPython have no GIL and can fully exploit multiprocessor systems

PyPy currently has a GIL like CPython

in Cython the GIL exists, but can be released temporarily using a "with" statement
weyou
2018-01-31 15:07:48 +08:00
@crysislinux 你怕不是对 python 的线程有什么误解吧。不管是 python 还是 Qt,任何线程里 sleep,其他线程都不可能卡住啊。Python 的线程也是货真价实的系统线程啊。只不过 python 的 GIL 做了解释器级别的同步,在非 IO 的情况下硬生生弄成了单线程,但本质上还是多线程。对于 sleep 这个操作来说,还是会真的挂起线程释放 CPU 到其他线程的。
justou
2018-01-31 15:46:36 +08:00
QThread 的最大作用是可以发送信号, 利用信号-槽机制可以方便的与 GUI 跨线程交互, 从这个方面来抉择使用哪种线程,
用一个 QThread 来管理一堆 python 的线程或进程在 GUI 多线程编程中是一种比较方便的模式
XIVN1987
2018-01-31 16:00:09 +08:00
@justou
用 QTimer 直接在同一个线程里,根本不用发信号,,岂不更方便。。
brightguo
2018-01-31 16:21:41 +08:00
额,QTimer 是定时器,和多线程有啥关系。。。
给你一个复杂计算,界面不就卡死了了
XIVN1987
2018-01-31 16:34:57 +08:00
@brightguo

嗯,好像有道理,,

不过大多数情况下用 QTimer 还是没问题的,,我好几个 PyQt 程序用 QTimer 替代多线程,运行良好
nicevar
2018-01-31 16:54:03 +08:00
@XIVN1987 额。。。QTimer 与线程有啥关系,怎么能代替线程呢,你的程序能用只能说明代码有问题或者不需要多线程,还可能已经有异步实现
用 QThread 肯定躲不过 GIL,假设 PyQt 程序信号量超级多,肯定会堵塞,但是 PyQt 都是些小程序,到不了这个地步
crysislinux
2018-01-31 16:58:01 +08:00
@weyou 你再仔细看看我说的呢,我的意思是在 QThread 里 sleep,这里只有 QThread,没有 Python 的 thread,要是代码实际上并没有在 QThread 里跑,那就应该卡住
wwqgtxx
2018-01-31 16:59:45 +08:00
@nicevar 其实说到“信号量超级多”这个问题,就算没有 GIL 他也一样会堵塞呀,因为 UI 线程还是只有一个,如果你要是在两个普通的 QThread 之间发送数据的话,那还是老老实实用 python 标准库的 queue 方便,反正都绕不过 GIL
justou
2018-01-31 17:30:06 +08:00
用 QTimer 来完成复杂任务避免 GUI 堵塞是一种摒弃的做法了, 一点都不 OOP, 随着任务变多变复杂整个代码只会变得一团糟. 在两种情形下用过 QTimer: 1) GUI 初始化时, 用 QTimer.singleShot(0, load_data)来加载数据避免界面半天不出来, 2) 纯定时器.

对于 1), 如果预处理比较复杂, 我宁愿放到单独的预处理线程, 界面只用于数据输入, 数据展示, 处理信号 /事件. 这样界面就很流畅

This is the traditional way of implementing heavy work in GUI applications, but as multithreading is nowadays becoming available on more and more platforms, we expect that zero-millisecond QTimer objects will gradually be replaced by QThreads.

http://doc.qt.io/qt-5/qtimer.html

另外, 线程中有 sleep 的代码充满着坏味道
nicevar
2018-01-31 17:30:26 +08:00
@wwqgtxx 相对于 Qt 程序,pyQt 有 GIL,线程越多 UI 线程的时间片就越有限,堵塞的临界点就不一样了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/427395

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX