在PyQt应用程序线程:使用Qt线程或Python线程?

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

[是的,我知道,现在我有两个问题 。]

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

编辑(rebounty):虽然在我的具体情况下的解决scheme可能会使用像Jeff Ober和LukášLalinskýbuild议的非阻塞networking请求(所以基本上将并发问题留给networking实现),但我仍然想要更多深入解答一般问题:

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


编辑2:谢谢你所有的答案。 虽然没有100%的一致意见,但似乎有一个普遍的共识,认为答案是“使用Qt”,因为它的好处是与图书馆的其他部分整合在一起,而没有真正的缺点。

对于想要在两个线程实现之间进行select的人,我强烈build议他们阅读这里提供的所有答案,包括方法链接到的PyQt邮件列表线程。

我为赏金有几个答案。 最后我select方丈的非常相关的外部参考; 但是,这是一个密切的电话。

再次感谢。

PyQt邮件列表中不久前曾经讨论过这个问题 。 引用Giovanni Bajo对这个话题的评论 :

大部分是一样的 主要区别在于QThreads更好地与Qt(asynchronous信号/插槽,事件循环等)集成。 另外,你不能在Python线程中使用Qt(你不能通过QApplication.postEvent把事件发送到主线程):你需要一个QThread来工作。

一般的经验法则可能是使用QThreads,如果你打算以某种方式与Qt进行交互,那么就使用Python线程。

还有一些对PyQt作者的这个话题的评论:“他们都是围绕相同的本地线程实现的包装”。 两个实现都以相同的方式使用GIL。

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

编辑:更多的线程

Python线程

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

重要提示:由于字节码指令的数量与程序中的行数不一致,这可能会产生误导。 即使在Python中,一个赋值也不是primefaces的,所以对于必须以primefaces方式执行的任何代码块,即使使用GIL,也需要互斥锁。

QT线程

当Python将控制交给第三方编译模块时,它释放GIL。 在需要的地方确保primefaces性是模块的责任。 当控制传回时,Python将使用GIL。 这可以使第三方库与线程混淆。 使用外部线程库更加困难,因为它增加了在模块和解释器之间何时何地进行控制的不确定性。

QT线程在释放GIL的情况下运行。 QT线程能够同时执行QT库代码(以及其他不能获取GIL的编译模块代码)。 但是,在QT线程的上下文中执行的Python代码仍然会获得GIL,现在您必须pipe理两组逻辑来locking您的代码。

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

非阻塞I / O

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

使用asynchronousI / O,您始终可以确保,对于每个打开的描述符,执行的path是一致的和有序的。 显然,有一些问题需要解决,比如当一个开放通道的代码依赖于另一个开放通道返回数据的代码的结果时,该怎么做。

基于事件的非阻塞I / O的一个很好的解决scheme是新的柴油机库。 目前仅限于Linux,但速度非常快,而且非常优雅。

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

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

如果你正在访问一个HTTP服务器,你应该考虑QNetworkAccessManager

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

如果你使用Qt,你需要使用QThread来使用Qt框架,特别是信号/插槽系统。

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

而且,这个select并没有很好的性能问题,因为它们都是C ++绑定。

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

我鼓励你使用QThread

杰夫有一些好点。 只有一个主线程可以执行任何GUI更新。 如果您确实需要从线程内更新GUI,则Qt-4的排队连接信号可以很容易地通过线程发送数据,并且在使用QThread时将自动调用; 我不确定如果你使用的是Python线程,他们会不会是这样,尽pipe给connect()添加一个参数很简单。

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

首先,CPython线程不会并发运行,至less不是Python代码。 是的,他们为每个Python线程创build了系统线程,但是只有当前持有全局解释器锁的线程才能运行(C扩展和FFI代码可能会绕过它,但是在线程不保留GIL的情况下不会执行Python字节码)。

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

为了进行额外的微调,可以修改在切换GIL所有权之前解释的字节码指令的数量 – 较低的值意味着更多的上下文切换(可能更高的响应性),但是每个线程的性能较低(上下文切换有成本 – 如果你尝试切换每隔几条指令,这对速度没有帮助。)

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

我不能评论Python和PyQt线程之间的确切区别,但我一直在做你正在尝试使用QThreadQNetworkAcessManager并确保在线程处于活动状态时调用QApplication.processEvents() 。 如果graphics用户界面的响应能力真的是你要解决的问题, 那么后者将会有所帮助。