按我的理解,由于 GIL 的存在,threading.Thread 肯定无法利用多核,任意时刻只有一个线程在跑; QThread 的话在 C++里肯定是能多个同时跑的,但在 PyQt 里它执行的也是 Python 代码,所以应该也和 threading 一样并不能真的并行;而 QTimer 似乎原本就是在创建它的那个线程里跑的,但既然大家都没法真的并行,那跟另外两个似乎是一回事儿,,而且 QTimer 写起来感觉更简洁、直观,也不用 sleep(),,所以我觉得写 PyQt 程序的时候并发逻辑用 QTimer 写就行了,,
1
crysislinux 2018-01-31 13:41:53 +08:00 via Android
qthread 是真线程。Python 只是包装,底下还是 c++在跑
|
2
XIVN1987 OP @crysislinux
可是 QThread 的 run 函数里的代码都是 python 代码啊,,比如: class TimeThread(QtCore.QThread): def run(self): # ... ... |
3
XIVN1987 OP v2ex 的回复真是,,,即不能修改、也不能预览,,
|
4
XIVN1987 OP |
5
crysislinux 2018-01-31 13:49:31 +08:00 via Android
@XIVN1987 这个还真没仔细想过。但是我以前用过,你可以试试在线程里 sleep,看看主线程 ui 会卡住不
|
6
XIVN1987 OP |
7
GeruzoniAnsasu 2018-01-31 13:55:39 +08:00
|
8
XIVN1987 OP |
9
GeruzoniAnsasu 2018-01-31 14:09:51 +08:00
@XIVN1987 emmmmm 好像的确是这样,但属于 qt 框架本身的东西可以绕开 python 封装,worker 线程间可能会被 gil 影响,但 gui 的部分应该影响不着
当然了我也是猜的,确实没细想过这种问题 |
10
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 |
11
weyou 2018-01-31 15:07:48 +08:00
@crysislinux 你怕不是对 python 的线程有什么误解吧。不管是 python 还是 Qt,任何线程里 sleep,其他线程都不可能卡住啊。Python 的线程也是货真价实的系统线程啊。只不过 python 的 GIL 做了解释器级别的同步,在非 IO 的情况下硬生生弄成了单线程,但本质上还是多线程。对于 sleep 这个操作来说,还是会真的挂起线程释放 CPU 到其他线程的。
|
12
justou 2018-01-31 15:46:36 +08:00
QThread 的最大作用是可以发送信号, 利用信号-槽机制可以方便的与 GUI 跨线程交互, 从这个方面来抉择使用哪种线程,
用一个 QThread 来管理一堆 python 的线程或进程在 GUI 多线程编程中是一种比较方便的模式 |
14
brightguo 2018-01-31 16:21:41 +08:00
额,QTimer 是定时器,和多线程有啥关系。。。
给你一个复杂计算,界面不就卡死了了 |
15
XIVN1987 OP |
16
nicevar 2018-01-31 16:54:03 +08:00
@XIVN1987 额。。。QTimer 与线程有啥关系,怎么能代替线程呢,你的程序能用只能说明代码有问题或者不需要多线程,还可能已经有异步实现
用 QThread 肯定躲不过 GIL,假设 PyQt 程序信号量超级多,肯定会堵塞,但是 PyQt 都是些小程序,到不了这个地步 |
17
crysislinux 2018-01-31 16:58:01 +08:00
@weyou 你再仔细看看我说的呢,我的意思是在 QThread 里 sleep,这里只有 QThread,没有 Python 的 thread,要是代码实际上并没有在 QThread 里跑,那就应该卡住
|
18
wwqgtxx 2018-01-31 16:59:45 +08:00
@nicevar 其实说到“信号量超级多”这个问题,就算没有 GIL 他也一样会堵塞呀,因为 UI 线程还是只有一个,如果你要是在两个普通的 QThread 之间发送数据的话,那还是老老实实用 python 标准库的 queue 方便,反正都绕不过 GIL
|
19
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 的代码充满着坏味道 |
21
XIVN1987 OP @justou
感谢回复,, 可你也要考虑 PyQt 和 Qt 的区别,Qt 里面是有真正的并行执行的,,两个线程分别占一个 CPU 核、并行执行;这时候 QThread 当然比 QTimer 好 100 倍 可是 PyQt 里面由于 GIL 的问题,你开了 QThread 它和主线程也只是交替执行的,,并没有真正的并行,,这时候感觉线程和 QTimer 区别就没那么明显了 |
22
justou 2018-01-31 18:23:24 +08:00
@XIVN1987 如果是要在 PyQt 程序中进行密集计算的话, 我会用一个 QThread 来管理一些进程, 或者干脆把密集计算部分用 C++来实现; 如果线程只是进行 IO 操作的话, PyQt 的 QThread 和 C++的 QThread 跑起来感觉不到任何差别, 甚至可以利用协程, 这比 C++的 QThread 有更好的并发.
用 QThread 不用 Timer 很大程度上是从设计上考虑, 用 QThread 可以形成更好的程序结构. 用 QThread 当线程 /进程管理者, 负责跟界面的交互, 而不是生成一堆 QThread 去完成具体的任务 |
23
wwqgtxx 2018-01-31 18:47:50 +08:00 via iPhone
@justou 其实吧,要是能在 c++中无缝操作 python 的 list 和 dict 就完美了,可惜 python 并没有提供官方的 c++接口
|
24
weyou 2018-01-31 19:12:10 +08:00
@crysislinux 恩,可能有点误解了
|
26
wwqgtxx 2018-01-31 21:16:55 +08:00 via iPhone
@justou 有些时候希望把一些运算量大的部分用 c++写,但是数据源依然是来自 python 代码,而这个时候,如果需要处理大量数据,那么这些数据就一定是放在 list 或者 dict 中的,有些时候还要求返回的依然是一个 list,所以如果可以在 c++中像 std::vector 以及 std::map 中操作 list 和 map 那么互操作性就大大增加了
这点 Boost.Python 做的就挺好,但是整个 boost 类库有点过于庞大了,要是能拆分出来就完美了 |
27
justou 2018-01-31 22:07:31 +08:00
@wwqgtxx "大量数据"要看具体类型, 数值数据用 list 存储是极其低效的, 可以用标准库的 array 或 numpy 存储, 想要避开 GIL 处理字符串可以在 Cython 中直接使用 C++的容器(string, vector, map 等, http://docs.cython.org/en/latest/src/userguide/wrapping_CPlusPlus.html ). array 和 numpy 也可以很容易把底层指针暴露给 C++处理
没实际用过 boost python, 感觉不如 Cython 简便直观, 有空深入学习下. 要拆分出来应该很容易, 只需要单独编译 boost python 库就行了 http://www.boost.org/doc/libs/1_66_0/libs/python/doc/html/building/installing_boost_python_on_your_.html |
28
wizardforcel 2018-01-31 22:13:19 +08:00 via Android
qtimer 不是多线程,它是把消息定期插入消息队列,消息队列各个消息是串行的。
|
29
wizardforcel 2018-01-31 22:16:35 +08:00 via Android
@weyou 是限制在单核上的多线程。因为有调度,执行顺序还是随机的。
|
30
wwqgtxx 2018-02-01 08:09:02 +08:00 via iPhone
@justou 很多情况下存在数据来源不可控的问题,原来的代码是使用纯 python 代码写的,自然也就没有用 array 和 numpy 的容器,而有些运算是需要做一些修改源数组的,这时候如果再写一部分衔接代码在两种数据结构之间拷贝就不太划来了
|
31
wizardforcel 2018-02-01 13:08:11 +08:00 via Android
@XIVN1987 还是有区别的。比如 qtimer 里面长时间 io 操作会卡界面。而线程里不会,等 io 的时候它会让渡控制权。
再比如线程里面对字段或者全局变量的访问需要上锁,因为有调度,qtimer 就不用。 单核上的线程也是线程,不能假设它是严格串行的。 |
32
XIVN1987 OP |