Qt中的多线程技术

简介: 简述Qt 提供了许多类和函数来处理线程,下面我们总结下可以用来实现多线程应用程序的四种不同方式。简述QThread - 具有可选事件循环的低级 APIQThreadPool 和 QRunnable - 重用线程Qt Concurrent - 使用高级 APIWorkerScript - QML 中的线程选择适当的方法解决方案的比较示例用例

简述

Qt 提供了许多类和函数来处理线程,下面我们总结下可以用来实现多线程应用程序的四种不同方式。

QThread - 具有可选事件循环的低级 API

QThread 是 Qt 中所有线程控制的基础,每个 QThread 实例表示和控制一个线程。

QThread 可以直接实例化或子类,实例化 QThread 提供了一个并行事件循环,允许在次线程中调用 QObject 的槽函数。子类化 QThread 允许应用程序在开始其事件循环之前初始化新的线程,或者运行没有事件循环的并行代码。

有关如何使用 QThread 的演示,请参阅 QThread 类参考和线程示例。

QThreadPool 和 QRunnable - 重用线程

经常创建和销毁线程可能是昂贵的,为了减少这种开销,现有线程可以重新用于新任务。QThreadPool 是可重用的 QThreads 的集合。

要在 QThreadPool 的线程中运行代码,请重新实现 QRunnable::run() 并实例化子类化的 QRunnable。使用 QThreadPool::start() 将 QRunnable 放在 QThreadPool 的运行队列中。当线程可用时,QRunnable::run() 中的代码将在该线程中执行。

每个 Qt 应用程序有一个全局线程池,可通过 QThreadPool::globalInstance() 进行访问。此全局线程池根据 CPU 中的核数自动维护最佳线程数。但是,可以显式创建和管理单独的 QThreadPool。

Qt Concurrent - 使用高级 API

Qt Concurrent 模块提供了处理一些常见并行计算模式的高级函数:map、filter 和 reduce。与使用 QThread 和 QRunnable 不同,这些函数不需要使用低级线程原语,例如:互斥锁或信号量。相反,他们返回一个 QFuture 对象,当他们准备就绪时,可用于检索函数的结果。QFuture 也可以用于查询计算进度并暂停/恢复/取消计算。为方便起见,QFutureWatcher 通过信号和槽与 QFutures 进行交互。

Qt Concurrent 的 map、filter 和 reduce 算法在所有可用的处理器核心之间自动分配计算,因此今天编写的应用程序将在以后在具有更多核心的系统上部署时继续扩展。

这个模块还提供了 QtConcurrent::run() 函数,它可以在另一个线程中运行任何函数。但是,QtConcurrent::run() 只支持可用于 map、filter 和 reduce 函数的一个子集。QFuture 可以用于检索函数的返回值,并检查线程是否正在运行。但是,对 QtConcurrent::run() 的调用仅使用一个线程,不能被暂停/恢复/取消,并且不能被查询进度。

有关各个功能的详细信息,请参阅 Qt Concurrent 模块文档。

WorkerScript - QML 中的线程

WorkerScript QML 类型允许 JavaScript 代码与 GUI 线程并行运行。

每个 WorkerScript 实例可以有一个 .js 脚本附加到它。当调用 WorkerScript::sendMessage() 时,脚本将在单独的线程(和单独的 QML 上下文)中运行。当脚本完成运行时,它可以发送一个回复给 GUI 线程,它将调用 WorkerScript::onMessage() 信号处理程序。

使用 WorkerScript 类似于使用已移动到另一个线程的工作 QObject。 数据通过信号在线程之间传输。

有关如何实现脚本以及可以在线程之间传递的数据类型的列表的详细信息,请参阅 WorkerScript 文档。

选择适当的方法

如上所述,Qt 为开发线程应用程序提供了不同的解决方案。给定应用程序的正确解决方案取决于新线程的用途和线程的生命周期。下面是 Qt 的线程技术的比较,接下来是一些示例用例的推荐解决方案。

解决方案的比较

特性 QThread QRunnable 和 QThreadPool QtConcurrent::run() Qt Concurrent (Map, Filter, Reduce) WorkerScript
语言 C++ C++ C++ C++ QML
可以指定线程优先级
线程可以运行事件循环
线程可以通过信号接收数据更新 是(由 worker QObject 接收) 是(由 WorkerScript 接收)
线程可以使用信号控制 是(由 QThread 接收) 是(由 QFutureWatcher 接收)
线程可以通过 QFuture 监视 部分
内置的暂停/恢复/取消功能

示例用例

生命周期 操作 解决方案
一次调用 在另一个线程中运行一个新的线性函数,可选择在运行过程中更新进度。 Qt 提供了不同的解决方案:
1. 将函数放在 QThread::run() 的重新实现中,并启动 QThread,发射信号来更新进度。
2. 将函数放在 QRunnable::run() 的重新实现中,并将 QRunnable 添加到 QThreadPool,写入线程安全的变量来更新进度。
3. 使用 QtConcurrent::run() 运行函数,写入线程安全的变量来更新进度。
一次调用 在另一个线程中运行现有函数并获取其返回值 使用 QtConcurrent::run() 运行函数,当函数返回时,有一个 QFutureWatcher 发出 finished() 信号,并调用 QFutureWatcher::result() 获取函数的返回值。
一次调用 使用所有可用内核对容器的所每一项执行操作。例如,从图像列表中生成缩略图。 使用 Qt Concurrent 的 QtConcurrent::filter() 函数来选择容器元素,并使用 QtConcurrent::map() 函数来对每个元素应用一个操作。若要将输出折叠成单一的结果,改用 QtConcurrent::filteredReduced() 和QtConcurrent::mappedReduced() 来代替。
一次调用/持久运行 在纯 QML 应用程序中执行长计算,并在结果准备好时更新 GUI。 将计算代码放在 .js 脚本中,并将其附加到 WorkerScript 实例。调用 sendMessage() 在一个新的线程中启动计算,也让脚本调用 WorkerScript::sendMessage(),将结果传递给 GUI 线程,处理 onMessage 中的结果并更新 GUI。
持久运行 有一个对象生活在另一个线程中,可以根据请求执行不同的任务和/或可以接收新的数据。 子类化一个 QObject 以创建 worker。实例化这个 worker 对象和一个 QThread,将 worker 移动到新线程,通过 queued 信号槽连接将命令或数据发送到 worker 对象。
持久运行 在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。 在 QThread::run() 的重新实现中直接编写无限循环,启动没有事件循环的线程,让线程发出信号将数据发送回 GUI 线程。
目录
相关文章
|
26天前
|
编解码 UED
Qt侧边栏的动态切换:隐藏与显示技术详解
在现代用户界面设计中,侧边栏(Sidebar)是一个常见的组件,它为用户提供了导航和工具面板的功能。在某些应用场景下,我们可能需要动态地隐藏或显示侧边栏,以优化界面布局或提供更灵活的用户体验。本文将分享如何在Qt框架下实现侧边栏的隐藏与呈现,包括技术细节和代码示例。
101 3
|
5月前
|
安全 Python
告别低效编程!Python线程与进程并发技术详解,让你的代码飞起来!
【7月更文挑战第9天】Python并发编程提升效率:**理解并发与并行,线程借助`threading`模块处理IO密集型任务,受限于GIL;进程用`multiprocessing`实现并行,绕过GIL限制。示例展示线程和进程创建及同步。选择合适模型,注意线程安全,利用多核,优化性能,实现高效并发编程。
78 3
|
26天前
|
UED
Qt侧边栏的动态展示:隐藏与呈现技术详解
在现代用户界面设计中,侧边栏(Sidebar)是一个常见的组件,它为用户提供了便捷的导航和操作入口。在Qt框架中,实现侧边栏的隐藏与呈现不仅能够提升应用的美观度,还能增强用户体验。本文将详细介绍如何在Qt中实现侧边栏的动态隐藏与呈现,包括技术要点和代码实现。
57 0
|
3月前
|
网络协议 安全
QT多线程
本文详细介绍了在Qt中如何正确使用QThread以及信号槽跨线程的使用方式,包括线程的正确退出方法和QObject在不同线程中创建子对象时可能遇到的问题。同时,文章还提供了相关博客和资料的链接,用于进一步学习和参考。
|
3月前
|
数据库 数据库管理
qt对sqlite数据库多线程的操作
本文总结了在Qt中进行SQLite数据库多线程操作时应注意的四个关键问题,包括数据库驱动加载、加锁、数据库的打开与关闭,以及QsqlQuery变量的使用。
218 1
|
2月前
|
网络协议 安全 Java
难懂,误点!将多线程技术应用于Python的异步事件循环
难懂,误点!将多线程技术应用于Python的异步事件循环
81 0
|
3月前
|
监控 Java
线程池中线程异常后:销毁还是复用?技术深度剖析
在并发编程中,线程池作为一种高效利用系统资源的工具,被广泛用于处理大量并发任务。然而,当线程池中的线程在执行任务时遇到异常,如何妥善处理这些异常线程成为了一个值得深入探讨的话题。本文将围绕“线程池中线程异常后:销毁还是复用?”这一主题,分享一些实践经验和理论思考。
160 3
|
3月前
自己动手写QT多线程demo
本文是作者关于如何编写Qt多线程demo的教程,介绍了如何实现多线程功能,包括可暂停和继续的功能。文章提供了部分示例代码,展示了如何创建线程类、启动和管理线程,以及线程间的通信。同时,还提供了相关参考资料和免费下载链接。
|
4月前
|
API Windows
揭秘网络通信的魔法:Win32多线程技术如何让服务器化身超级英雄,同时与成千上万客户端对话!
【8月更文挑战第16天】在网络编程中,客户/服务器模型让客户端向服务器发送请求并接收响应。Win32 API支持在Windows上构建此类应用。首先要初始化网络环境并通过`socket`函数创建套接字。服务器需绑定地址和端口,使用`bind`和`listen`函数准备接收连接。对每个客户端调用`accept`函数并在新线程中处理。客户端则通过`connect`建立连接,双方可通过`send`和`recv`交换数据。多线程提升服务器处理能力,确保高效响应。
59 6
|
4月前
三个线程交替打印ABC:技术深度解析与实战应用
【8月更文挑战第14天】在并发编程中,实现多个线程之间的精确协同工作是一项既具挑战性又极具实用价值的任务。今天,我们将深入探讨一个经典问题:如何使用三个线程交替打印字符A、B、C,且每个字符连续打印三次,之后循环进行。这个问题不仅考验了我们对线程同步机制的理解,还锻炼了我们在复杂并发场景下的设计能力。
83 0