在PyQt应用程序中进行线程化:使用Qt线程还是Python线程?

时间:2009-10-20 15:54:20

标签: python multithreading pyqt

我正在编写一个GUI应用程序,它定期通过Web连接检索数据。由于此检索需要一段时间,因此会导致UI在检索过程中无响应(无法将其拆分为较小的部分)。这就是为什么我想将Web连接外包给一个单独的工作线程。

[是的,我知道,现在我有two problems。]

无论如何,应用程序使用PyQt4,所以我想知道更好的选择是什么:使用Qt的线程或使用Python threading模块?每个的优点/缺点是什么?或者你有完全不同的建议吗?

修改(重新计算):虽然我的特定情况下的解决方案可能会使用非阻塞网络请求,例如Jeff OberLukáš Lalinský建议(所以基本上离开对网络实现的并发性问题),我仍然希望对一般问题有更深入的回答:

使用PyQt4(即Qt)线程优于本机Python线程(来自threading模块)有什么优缺点?


编辑2:谢谢大家的回答。虽然没有100%的协议,但似乎普遍认为答案是“使用Qt”,因为它的优点是与库的其余部分集成,同时没有造成任何真正的缺点。

对于任何想要在两个线程实现之间进行选择的人,我强烈建议他们阅读此处提供的所有答案,包括abbot链接到的PyQt邮件列表线程。

我为赏金考虑了几个答案;最后,我选择了abbot作为非常相关的外部参考;然而,这是一个近距离的电话。

再次感谢。

7 个答案:

答案 0 :(得分:98)

这是discussed不久前在PyQt邮件列表中。引用Giovanni Bajo关于这个主题的comments

  

它大致相同。主要区别在于QThreads更好   与Qt集成(异步信号/时隙,事件循环等)。   此外,您不能使用Python线程中的Qt(例如,您不能   通过QApplication.postEvent将事件发布到主线程:你   需要一个QThread才能工作。

     

一般的经验法则是,如果您要以某种方式与Qt进行交互,则使用QThreads,否则使用Python线程。

之前有一些关于PyQt作者对此主题的评论:“它们都是围绕相同本机线程实现的包装器”。两种实现都以相同的方式使用GIL。

答案 1 :(得分:31)

Python的线程将更简单,更安全,并且由于它适用于基于I / O的应用程序,因此它们可以绕过GIL。也就是说,您是否考虑过使用Twisted或非阻塞套接字/ select?的非阻塞I / O?

编辑:线程更多

Python主题

Python的线程是系统线程。但是,Python使用全局解释器锁(GIL)来确保解释器一次只执行一定大小的字节码指令块。幸运的是,Python在输入/输出操作期间释放GIL,使线程可用于模拟非阻塞I / O.

重要警告:这可能会产生误导,因为的字节码指令数与程序中的行数相对应。即使单个赋值在Python中也可能不是原子的,因此对于必须以原子方式执行的任何代码块都需要互斥锁,即使使用GIL也是如此。

QT主题

当Python将控制权移交给第三方编译模块时,它会释放GIL。模块的责任是在需要时确保原子性。当传回控件时,Python将使用GIL。这可能会使第三方库与线程混淆使用。使用外部线程库变得更加困难,因为它增加了模块与解释器控制的位置和时间的不确定性。

QT线程在GIL发布的情况下运行。 QT线程能够同时执行QT库代码(以及其他未获取GIL的编译模块代码)。但是,在QT线程的上下文中执行的Python代码仍然获取GIL,现在您必须管理两个逻辑集来锁定代码。

最后,QT线程和Python线程都是围绕系统线程的包装器。 Python线程使用起来比较安全,因为那些不是用Python编写的部分(隐式使用GIL)在任何情况下都使用GIL(虽然上面的警告仍然适用。)

非阻止I / O

线程为您的应用程序增加了极大的复杂性。特别是在处理Python解释器和编译模块代码之间已经很复杂的交互时。虽然许多人发现难以遵循基于事件的编程,但基于事件的非阻塞I / O通常比线程更难以推理。

使用异步I / O,您始终可以确保对于每个打开的描述符,执行路径是一致且有序的。显然,必须解决一些问题,例如当依赖于一个开放通道的代码进一步依赖于另一个开放通道返回数据时要调用的代码结果时该怎么办。

基于事件的非阻塞I / O的一个不错的解决方案是新的Diesel库。目前它仅限于Linux,但它非常快速且非常优雅。

值得花时间学习pyevent,这是一个很棒的libevent库的包装器,它为使用最快的系统方法(在编译时确定)提供了基于事件的编程的基本框架。

答案 2 :(得分:21)

QThread的优势在于它与Qt库的其余部分集成在一起。也就是说,Qt中的线程感知方法需要知道它们在哪个线程中运行,并且要在线程之间移动对象,您需要使用QThread。另一个有用的功能是在线程中运行自己的事件循环。

如果您正在访问HTTP服务器,则应考虑QNetworkAccessManager

答案 3 :(得分:13)

我在PyTalk工作时问自己同样的问题。

如果您使用的是Qt,则需要使用QThread才能使用Qt框架,特别是信号/插槽系统。

使用信号/插槽引擎,您将能够从一个线程与另一个线程以及项目的每个部分进行通信。

此外,由于两者都是C ++绑定,因此没有关于此选择的性能问题。

这是我对PyQt和线程的经验。

我鼓励您使用QThread

答案 4 :(得分:9)

杰夫有一些好处。只有一个主线程可以执行任何GUI更新。如果你确实需要从线程内更新GUI,Qt-4的queued connection信号可以很容易地跨线程发送数据,并且如果你正在使用QThread,它将自动被调用;我不确定如果你正在使用Python线程,它们是否会存在,尽管可以很容易地将参数添加到connect()

答案 5 :(得分:5)

我也不能推荐,但我可以尝试描述CPython和Qt线程之间的差异。

首先,CPython线程不会同时运行,至少不是Python代码。是的,他们确实为每个Python线程创建了系统线程,但是只允许当前持有Global Interpreter Lock的线程运行(C扩展和FFI代码可能绕过它,但是当线程不保持GIL时不执行Python字节码)。

另一方面,我们有Qt线程,它们基本上是系统线程上的公共层,没有Global Interpreter Lock,因此能够并发运行。我不确定PyQt如何处理它,但是除非你的Qt线程调用Python代码,否则它们应该能够并发运行(禁止可能在各种结构中实现的各种额外锁)。

对于额外的微调,您可以修改在切换GIL所有权之前解释的字节码指令的数量 - 较低的值意味着更多的上下文切换(可能更高的响应性),但每个单独的线程的性能更低(上下文切换具有其成本) - 如果你尝试切换每一条指令,它都无助于加速。)

希望它有助于解决您的问题:)

答案 6 :(得分:0)

我无法评论Python和PyQt线程之间的确切差异,但我一直在使用QThreadQNetworkAcessManager执行您尝试执行的操作,并确保调用{{ 1}}线程还活着。如果GUI响应性确实是您要解决的问题,the later将有所帮助。