多线程模块能够更加高效得完成任务,但是在PyQt 应用程序中实现多线程可以使用 Qt 的线程模块(QThread)或者 Python 的 threading 模块。两者各有优劣,具体选择取决于项目需求和个人偏好。下面我们将以案例来说明两种模块具体得优缺点。
1、问题背景
在 PyQt 应用程序中,编写了一个定期通过 web 连接检索数据的 GUI 应用程序。由于检索过程需要一段时间,因此导致在检索过程中 UI 无响应(无法将其拆分为更小的部分)。因此,需要将 web 连接外包给单独的工作线程。
2、解决方案
- Qt 线程:
- 优势:
- 与 Qt 库的其余部分更好地集成。例如,Qt 中具有线程感知的方法需要知道它们在哪个线程中运行,并且要在线程之间移动对象,则需要使用 QThread。
- 另一个有用的功能是在线程中运行自己的事件循环。
- 劣势:
- 可能无法从 Python 线程访问 Qt。例如,无法通过 QApplication.postEvent 将事件发布到主线程。
- 优势:
- Python 线程:
- 优势:
- 更简单、更安全。由于这是一个基于 I/O 的应用程序,因此它们能够绕过 GIL。
- Python 线程是系统线程。但是,Python 使用全局解释器锁 (GIL) 来确保一次只执行一定大小的字节码指令块。幸运的是,Python 在输入/输出操作期间会释放 GIL,从而使线程对于模拟非阻塞 I/O 很有用。
- 劣势:
- 由于 Python 的内置锁 GIL(全局解释器锁),Python 线程不能真正并发执行 Python 代码,包括了调用 Python API 和解释 Python 字节码。
- 多线程编程使应用程序复杂度大增,尤其是在处理 Python 解释器和编译模块代码之间的本来就复杂的交互时。
- 优势:
- 非阻塞 I/O:
- 通过异步 I/O,可以始终确保每个打开的文件描述符的执行路径一致且有序。
- 例如:
- QNetworkAccessManager:如果应用程序正在访问 HTTP 服务器,则应该考虑 QNetworkAccessManager。
- Twisted 或非阻塞套接字/select:可以考虑使用 Twisted 或非阻塞套接字/select 实现非阻塞 I/O。
- Diesel 库:它目前仅限于 Linux,但它的速度非常快且非常优雅。
- pyevent:它是 libevent 库的包装器,它提供了一个基本框架,用于使用系统最快的可用方法(在编译时确定)进行基于事件的编程。
在 PyQt 应用程序中使用线程时,需要考虑以下几点:
- 如果需要从线程内更新 GUI,则应使用 Qt-4 的队列连接信号,以便轻松地跨线程发送数据,并且如果使用 QThread,则会自动调用它们;不确定如果使用 Python 线程是否会调用它们,尽管很容易为 connect() 添加一个参数。
- 只有一个主线程可以进行任何 GUI 更新。
- Qt 线程与 Python 线程的主要区别在于,Qt 线程更好地集成到 Qt 库的其余部分。也就是说,Qt 中的线程感知方法需要知道它们在哪个线程中运行,并且要在线程之间移动对象,则需要使用 QThread。
- Qt 线程在没有 Global Interpreter Lock 的情况下运行,因此能够并发运行。
- Python 线程不需要 Global Interpreter Lock,因此能够并发运行。
- 如果 Qt 线程不调用 Python 代码,则它们应该能够并发运行(除了可能在各种结构中实现的各种额外锁之外)。
通过上述了解,我们应该清晰得知道,如果你的应用程序主要是 CPU 密集型任务,可能更倾向于使用 multiprocessing 模块。对于大多数 GUI 应用程序而言,使用 QThread 通常是更好的选择,因为它更好地与 Qt 框架集成,并提供了方便的线程间通信机制。所以说具体情况还得看项目要求,如果有不懂得可以评论区留言讨论。