赞
踩
- QThread
- QThreadPool和QRunnable(重用线程)
- Qt Concurrent
- WorkerScript(QML中的线程)
QThread 在上两篇文章中已经解释了,这里就不再赘述。
QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由 run 函数的重新实现表示。
autoDelete() | 返回是否自动删除 |
setAutoDelete() | 设置自动删除(thue) |
设置自动删除的话,在QThreadPool中会自动删除QRunnable
使用的注意事项:
启用自动删除时,使用相同的 QRunnable 多次调用 QThreadPoool::start() 会产生争用条件,不建议这样做。
顾名思义,是有很多线程的池子, 可以管理池子中的线程。
线程池的优点:
- 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控。
常用函数:
setMaxThreadCount() | 设置线程池中最大的线程数,默认:QThread::idealThreadCount |
setExpiryTimeout(int) | 将超时xx毫秒未使用的线程视为已过期并将退出,已在运行的线程没有影响。建议在创建线程池后立即设置。 |
setStackSize() | 设置线程池工作线程的堆栈大小,当线程池创建新线程时使用, 默认值为 0 |
clear() | 清除队列中未开启的可运行项 |
globalInstance() | 返回一个全局QThreadPool实例 |
reserveThread() | 保留一个线程( 此函数将始终增加活动线程数) |
releaseThread() | 释放reserveThread()保留的线程 |
start(QRunnable,int) | 开启一个线程,可以设置优先级 |
tryStart() | 尝试开启一个线程 |
tryTake() | 尝试从队列中删除指定的尚未启动的可运行对象(谨慎使用) |
waitForDone(int) | 等待所有线程退出最多毫秒毫秒,并从线程池中删除所有线程。如果删除了所有线程返回true |
activeThreadCount() | 当前活跃的线程数 |
- 使用QThreadPool::globalInstance()->start()启动 (全局)
- 创建一个QThreadPool 对象,然后通过该对象启动
Runnable类:
- #ifndef RUNNABLE_H
- #define RUNNABLE_H
- #include<QThread>
- #include<QDebug>
- #include<QRunnable>
-
- class Runnable:public QRunnable
- {
- public:
- explicit Runnable()//构造函数
- {
-
- }
- ~Runnable()//析构函数
- {
-
- }
- void run()//重写run函数
- {
- qDebug()<<"Runnable中的run函数";
- qDebug()<<"thread to run():"<<QThread::currentThreadId();
- }
- };
- #endif // RUNNABLE_H
1.使用QThreadPool::globalInstance()->start()启动 (全局)
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- qDebug()<<"主线程号:"<<QThread::currentThread();
- Runnable *able=new Runnable;
- QThreadPool::globalInstance()->start(able);
-
- }
2.创建一个QThreadPool 对象,然后通过该对象启动
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- qDebug()<<"主线程号:"<<QThread::currentThread();
- Runnable *able=new Runnable;
- //QThreadPool::globalInstance()->start(able);
- QThreadPool pool;
- pool.start(able);
-
- }
- QThreadPool pool;
- qDebug()<<"本机的线程数"<<QThread::idealThreadCount();
- qDebug()<<"线程池中的线程数"<<pool.maxThreadCount();
- pool.setMaxThreadCount(10);//设置线程池中最大的线程数
- pool.setExpiryTimeout(40000);//如果线程40000毫秒,40秒未使用,使它过期
- Runnable *runnable=new Runnable;
- pool.reserveThread();//保留一个线程
- pool.start(runnable);//开启线程
- qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();
- pool.releaseThread();//释放那个保留的线程
- qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();
QRunnable可以执行完释放内存:需要将autoDelete()设置为true,默认为true。
- #ifndef RUNNABLE_H
- #define RUNNABLE_H
- #include<QThread>
- #include<QDebug>
- #include<QRunnable>
-
- class Runnable:public QRunnable
- {
- public:
- explicit Runnable()//构造函数
- {
-
- }
- ~Runnable()//析构函数
- {
- qDebug()<<__FUNCTION__;
- }
- void run()//重写run函数
- {
- qDebug()<<"thread to run():"<<QThread::currentThreadId();
- }
- };
- #endif // RUNNABLE_H
- QThreadPool pool;
- Runnable *runnable=new Runnable;
- pool.start(runnable);//开启线程
执行完后自动调用析构函数。
由于QRunnable是单独的一个类,并没有继承QObject,所以没有connect()函数,所以无法于外界进行通信。
解决该问题的方法:
- 使用QMetaObject::invokeMethod()
- 使用多继承的方式,同时继承QRunnable和QObject,但这样的话会使接口混乱(尽量不要使用)
QMetaObject::invokeMethod()的创建方法 :
- QMetaObject::invokeMethod(QObject *obj, //使用的类
- const char *member, //调用函数名
- Qt::ConnectionType type,//调用类型
- QGenericReturnArgument ret,//用来存储返回值的类型
- QGenericArgument val0 = QGenericArgument(nullptr),
- QGenericArgument val1 = QGenericArgument(),
- QGenericArgument val2 = QGenericArgument(),
- QGenericArgument val3 = QGenericArgument(),
- QGenericArgument val4 = QGenericArgument(),
- QGenericArgument val5 = QGenericArgument(),
- QGenericArgument val6 = QGenericArgument(),
- QGenericArgument val7 = QGenericArgument(),
- QGenericArgument val8 = QGenericArgument(),
- QGenericArgument val9 = QGenericArgument())
- //最多有10个参数
*obj:需要把使用类传进去
member:函数的名称
type:调用类型:
- Qt::DirectConnnect 立即调用该成员函数
- Qt::QueuedConnection 一旦应用程序进入主事件循环,就会发送 QEvent 并调用成员。
- Qt::BlockQueuedConnection 当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
- Qt::AutoConnection 如果 obj 与调用方位于同一线程中,则同步调用成员;否则,它将异步调用该成员。
val0-val9:最多可以有10个参数
传参的方式:使用宏传参
- Q_ARG(type,const Type&value)
- Q_RETURN_ARG(type, Type&value)
- QString S="hellow";
- QMetaObject::invokeMethod(obj,
- "Text",
- Qt::DirectConnection,
- Q_ARG(QString,"pppp"),
- Q_RETURN_ARG(QString,S));
- #ifndef RUNNABLE_H
- #define RUNNABLE_H
- #include "widget.h"
- #include<QThread>
- #include<QObject>
- #include<QDebug>
- #include<QRunnable>
- #include<QMetaObject>
-
- class Runnable:public QRunnable
- {
- public:
- explicit Runnable(QObject *obj1):obj(obj1)//构造函数
- {
-
-
- }
- ~Runnable()//析构函数
- {
-
- }
- void run()//重写run函数
- {
- QString S="hellow";
- QMetaObject::invokeMethod(obj,"Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));
- qDebug()<<"thread to run():"<<QThread::currentThreadId();
- }
- QObject *obj=nullptr;//需要传的对象
- };
- #endif // RUNNABLE_H
主函数:
主类中创建函数开头需要添加:Q_INVOKABLE(这样才直到调用哪一个函数)
- #include "widget.h"
- #include "ui_widget.h"
- #include"runnable.h"
- #include<QThreadPool>
- class Rnnable;
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- QThreadPool pool;
- Runnable *runnable=new Runnable(this);//传入调用的对象
- pool.start(runnable);//开启线程
- }
- Q_INVOKABLE void Widget::Text(QString a,QString b)//创建一个函数,名称要相同
- {
- qDebug()<<a;
- qDebug()<<b;
- }
- Widget::~Widget()
- {
- delete ui;
- }
-
QtConcurrent命名空间提供高级 API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。
使用 QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数。这意味着编写的应用程序在部署到多核系统时将自动扩展。
1.pro文件中添加
QT +=concurrent
2. 添加头文件
#include<QtConcurrent>
使用QtConcurrent::run()函数执行内容
函数模型:
- //T为模板类型
-
- QFuture<T> QtConCurrent::run(Function)//执行一个函数
-
- QFuture<T> QtConCurrent::run(threadpool,Function)//线程池中,取出线程执行function函数
- void text()
- {
- qDebug()<<"ok";
- }
- void text1(int a)
- {
- qDebug()<<a;
- }
- void text2(QString s1,QString s2)
- {
- qDebug()<<s1+s2;
- }
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
-
- QFuture<void> fu=QtConcurrent::run(text);
- QFuture<void> fu1=QtConcurrent::run(text1,10);
- QFuture<void> fu2=QtConcurrent::run(text2,QString("aa"),QString("bb")");
- fu.waitForFinished();//等待完成
- fu1.waitForFinished();//等待完成
- fu2.waitForFinished();//等待完成
-
- return a.exec();
- }
- void text1(int a)
- {
- qDebug()<<a;
- }
-
- int main(int argc, char *argv[])
- {
- QApplication a(argc, argv);
- QThreadPool pool;
- QFuture<void> fu=QtConcurrent::run(&pool,text1,10);
- fu.waitForFinished();//等待完成
- return a.exec();
- }
run()函数可以接收成员函数的指针,第一个参数必须是常量引用或指向类实例的指针。
- QByteArray bytearray = "hello world";
- QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
- QList<QByteArray> result = future.result();
- QImage image = ...;
- QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels,QImage::InvertRgba);
- future.waitForFinished();
特征 | QThread | QRunnable和QThreadPool | QtConcurrent::run() | QtConcurrent (Map,Filter,Reduce) |
指定优先级 | 是 | 是 | ||
可运行 事件循环 | 是 | |||
线程可以通过 信号接收数据更新 | 是 | |||
可以使用 信号控制螺纹 | 是 | 是(QFutureWatcher) | ||
可以通过 QFuture监控线程 | 部分是 | 是 | ||
内置暂停/ 恢复/取消功能 | 是 |
生命周期 | 开发任务 | 解决方案 |
持续运行 | 在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。 | 直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号以将数据发送回 GUI 线程。 |
持续运行 | 让一个对象存在于另一个线程中,该线程可以根据请求执行不同的任务和/或可以接收要使用的新数据。 | 子类 一个QObject 以创建工作线程。实例化此工作线程对象和 QThread。将工作线程移动到新线程。通过排队的信号槽连接将命令或数据发送到工作器对象。 |
一次调用 | 在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。 | Qt提供不同的解决方案:
|
一次调用 | 在另一个线程中运行现有函数并获取其返回值。 | 使用 QtConcurrent::run() 运行函数。让 QFutureWatcher 在函数返回时发出 done() 信号,并调用 QFutureWatcher::result() 来获取函数的返回值。 |
一次调用 | 使用所有可用核心对容器的所有项执行操作。例如,从图像列表生成缩略图。 | 使用 Qt Concurrent 的 QtConcurrent::filter() 函数选择容器元素,使用 QtConcurrent::map() 函数对每个元素应用操作。要将输出折叠成单个结果,请改用 QtConcurrent::filteredReduction() 和 QtConcurrent::mappedReduction()。 |
参考文章:
Thread Support in Qt | Qt 5.15
Qt线程之QRunnable的使用详解_luoyayun361的博客-CSDN博客_qrunnable
【QT】继承QRunnable+QThreadPool实现多线程_李春港的博客-CSDN博客_继承qrunnable
Qt并发模块Qt Concurrent的使用_Amnesia Greens的博客-CSDN博客_qtconcurrent
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。