【Qt】Qt多线程开发—实现多线程设计的四种⽅法
Qt—使⽤Qt实现多线程设计的四种⽅法
⽂章⽬录
⼀、写在前⾯
这篇⽂章内容是:关于Qt多线程应⽤设计的。
在Qt中提供了四种⽅法来进⾏多线程编程和设计。合理的选择对应的⽅法来满⾜实际开发中的应⽤场景。
⼆、【⽅法⼀】 QThread:带有可选事件循环的底层API
QThread是Qt中所有线程控件的基础,每个QThread实例代表和控制着⼀个线程。使⽤QThread创建线程有两种⽅法:1)、可以直接实例化创建,2)也可以⼦类化它进⾏线程创建。
1、实例化QThread:提供了⼀个并⾏事件循环,允许在辅助线程中调⽤QObject槽函数。
2、继承QThread:允许应⽤程序在启动事件循环之前初始化新线程,或者在没有事件循环的情况下运⾏并⾏代码。thread技术
三、【⽅法⼆】 QThreadPool和QRunnable:重⽤线程
频繁的创建和销毁线程的代价可能会很⾼。为了减少这种开销,可以对新任务重⽤现有的线程。QThreadPool是可重⽤QThreads的集合。要在QThreadPool的⼀个线程中运⾏代码,需要重新实现QRunnable::run()并实例化⼦类化的QRunnable。
使⽤````QThreadPool::start()```将QRunnable放到QThreadPool的运⾏队列中。当线程可⽤时,QRunnable::run()中的代码将在该线程中执⾏。
【备注】:每个Qt应⽤程序都有⼀个全局线程池,可以通过QThreadPool::globalInstance()访问它。这个全局线程池根据CPU中的核⼼数量将⾃动维护最佳的线程数量。但是,在实际开发中,也可以显式地创建和管理⼀个单独的QThreadPool。
四、【⽅法三】Qt并发:使⽤⾼级API
Qt并发模块提供了许多⾼级功能,⽤来处理⼀些常见的并⾏计算模式。例如:map、filter和reduce。Qt并发与使
⽤QThread和QRunnable不同,这些函数不需要使⽤底层的线程原语,如互斥或信号量等。相反,它们返回的是⼀个QFuture对象,该对象可⽤于在线程准备或者完成时⾃动检索函数的结果;QFuture还可以
⽤来查询、计算进度和暂停/恢复/取消计算。为了⽅便起
见,QFutureWatcher允许通过信号和槽与QFutures进⾏交互。
Qt Concurrent的并⾏计算模型:map、filter和reduce等算法⾃动将计算分配到所有可⽤的处理器核⼼上,因此,我们今天编写的应⽤程序在以后部署到拥有更多内核的系统时将继续得以扩展和使⽤,这⾮常⽅便。
这个模块还提供了QtConcurrent::run()函数,它可以在另⼀个线程中运⾏任何的函数。但是,QtConcurrent::run()只⽀
持map、filter和reduce函数可⽤的特性⼦集,QFuture可⽤于检索函数的返回值并检查线程是否正在运⾏。
但是,对QtConcurrent::run()的调⽤只使⽤⼀个线程,不能暂停/恢复/取消,也不能查询进程。
五、【⽅法四】 WorkerScript:QML中的线程化
WorkerScript QML类型允许JavaScript代码与GUI线程并⾏运⾏。每个WorkerScript实例可以附加⼀个.js脚本。调
⽤WorkerScript.sendMessage()时,脚本将在单独的线程(和单独的QML上下⽂)中运⾏。当脚本运⾏完成时,它可以将⼀个回复发送回GUI线程,该线程将调⽤Message()信号处理程序。
使⽤WorkerScript类似于使⽤已移动到另⼀个线程的worker QObject,数据通过信号在线程之间进⾏传输。
【注】这种⽅法在QML中使⽤
六、如何选择上述四种不同的多线程设计的解决⽅案
如上所⽰,Qt为开发多线程应⽤程序提供了不同的解决⽅案。对于多线程应⽤程序的正确解决⽅案取决于:新线程的⽤途和线程的⽣命周期。下⾯是Qt⼏种多线程技术机制的⽐较:
序号特点QThread
QRunnable 和
QThreadPool
QtConcurrent::run()
Qt
Concurrent(Map/Filter/Reduce)
WorkerScript
1开发语⾔C++C++C++C++QML
2是否可以指定线
程优先级
Yes Yes
3线程是否可以运
⾏⼀个事件循环
Yes
4线程通过信号接
收数据更新
Yes (received
by a worker )
Yes (received
by )
5线程是否可以使
⽤信号来控制
Yes (received
by )
Yes (received by )
6线程是否可以通
过QFuture来监
控
部分地Yes
7是否拥有内置能
⼒:取消/暂停/
恢复
Yes
七、Qt多线程应⽤设计⽰例
线程
⽣命
周期
操作解决⽅法
⼀次调⽤在另⼀个线程中运⾏⼀个新的线性
函数,可以选择在运⾏期间进⾏进
度更新。
Qt提供了不同的解决⽅案: 将该函数放在QThread::run()的重新实现中,并启动QThread。发出信号更新进
度。将该函数放在QRunnable::run()的重新实现中,并将QRunnable添加到QThreadPool中。写⼊线程安全
的变量以更新进度。使⽤QtConcurrent:: Run()运⾏函数。写⼊线程安全的变量以更新进度。
⼀次调⽤在另⼀个线程中运⾏⼀个现有函数
并获取它的返回值。
使⽤QtConcurrent:: Run()运⾏函数。 让QFutureWatcher在函数返回时发出finished()信号,并调⽤
QFutureWatcher::result()来获取函数的返回值。
⼀次调⽤在另⼀个线程中运⾏⼀个现有函数
并获取它的返回值。
使⽤QtConcurrent:: Run()运⾏函数。让QFutureWatcher在函数返回时发出finished()信号,并调⽤
QFutureWatcher::result()来获取函数的返回值。
⼀次调⽤使⽤所有可⽤的内核对容器
(Container)的所有项执⾏操
作。例如,从图像列表⽣成缩略
图。
使⽤QtConcurrent的QtConcurrent::filter()函数来选择容器元素,使⽤QtConcurrent::map()函数来对每个
元素应⽤⼀个操作。要将输出换算成单个结果,可以使⽤QtConcurrent:: filteredreduce()和
QtConcurrent::mappedReduced()。
⼀次
调⽤/永久存在在纯QML应⽤程序中完成长时间
的计算,并在结果准备好时更新
GUI。
将计算代码放在.js脚本中,并将其附加到WorkerScript实例。调⽤WorkerScript.sendMessage()在新线程
中启动计算。让脚本也调⽤sendMessage(),将结果传递回GUI线程。在onMessage中处理结果并在那⾥更
新GUI。
永久存在在另⼀个线程中有⼀个对象,它可
以根据请求执⾏不同的任务,并
且/或者可以接收新的数据来处
理。
⼦类化⼀个QObject来创建⼀个worker。实例化这个worker对象和⼀个QThread。将worker移动到新线程。
通过排队的信号和槽连接向worker对象发送命令或数据。
永久存在在另⼀个线程中重复执⾏开销较⼤
的操作,其中该线程不需要接收任
何信号或事件。
直接在QThread::run()的重新实现中写⼊⽆限循环。在没有事件循环的情况下启动线程。让线程发出信号将数
据发送回GUI线程。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论