当前位置:   article > 正文

Qt中的多线程_qt 多线程

qt 多线程

Qt中有多种方法实现多线程:

  1. QThread
  2. QThreadPool和QRunnable(重用线程)
  3. Qt Concurrent
  4. WorkerScript(QML中的线程)

QThread 在上两篇文章中已经解释了,这里就不再赘述。 

QThreadPoo和QRunnable(实现的多线程)

QRunnable:

QRunnable 类是一个接口,用于表示需要执行的任务或代码段,由 run 函数的重新实现表示。

autoDelete()返回是否自动删除
setAutoDelete()设置自动删除(thue)

设置自动删除的话,在QThreadPool中会自动删除QRunnable

 使用的注意事项:

 启用自动删除时,使用相同的 QRunnable 多次调用 QThreadPoool::start() 会产生争用条件,不建议这样做。

QRunnable和QThread的区别:

  

 QThreadPool(线程池)

顾名思义,是有很多线程的池子, 可以管理池子中的线程。

线程池的优点:

  1. 降低资源消耗。 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。 当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控。
  4.   

常用函数: 

setMaxThreadCount()  

设置线程池中最大的线程数,默认:QThread::idealThreadCount
setExpiryTimeout(int)将超时xx毫秒未使用的线程视为已过期并将退出,已在运行的线程没有影响。建议在创建线程池后立即设置。
setStackSize()       设置线程池工作线程的堆栈大小,当线程池创建新线程时使用, 默认值为 0
clear()清除队列中未开启的可运行项
globalInstance()返回一个全局QThreadPool实例
reserveThread()保留一个线程( 此函数将始终增加活动线程数)
releaseThread()释放reserveThread()保留的线程
start(QRunnable,int)开启一个线程,可以设置优先级
tryStart()尝试开启一个线程
tryTake()尝试从队列中删除指定的尚未启动的可运行对象(谨慎使用)
waitForDone(int)等待所有线程退出最多毫秒毫秒,并从线程池中删除所有线程。如果删除了所有线程返回true
activeThreadCount()当前活跃的线程数

启动QRunnable线程的方法: 

  1. 使用QThreadPool::globalInstance()->start()启动   (全局)
  2. 创建一个QThreadPool 对象,然后通过该对象启动

Runnable类:

  1. #ifndef RUNNABLE_H
  2. #define RUNNABLE_H
  3. #include<QThread>
  4. #include<QDebug>
  5. #include<QRunnable>
  6. class Runnable:public QRunnable
  7. {
  8. public:
  9. explicit Runnable()//构造函数
  10. {
  11. }
  12. ~Runnable()//析构函数
  13. {
  14. }
  15. void run()//重写run函数
  16. {
  17. qDebug()<<"Runnable中的run函数";
  18. qDebug()<<"thread to run():"<<QThread::currentThreadId();
  19. }
  20. };
  21. #endif // RUNNABLE_H

1.使用QThreadPool::globalInstance()->start()启动   (全局)

  1. Widget::Widget(QWidget *parent)
  2. : QWidget(parent)
  3. , ui(new Ui::Widget)
  4. {
  5. ui->setupUi(this);
  6. qDebug()<<"主线程号:"<<QThread::currentThread();
  7. Runnable *able=new Runnable;
  8. QThreadPool::globalInstance()->start(able);
  9. }

  

2.创建一个QThreadPool 对象,然后通过该对象启动

  1. Widget::Widget(QWidget *parent)
  2. : QWidget(parent)
  3. , ui(new Ui::Widget)
  4. {
  5. ui->setupUi(this);
  6. qDebug()<<"主线程号:"<<QThread::currentThread();
  7. Runnable *able=new Runnable;
  8. //QThreadPool::globalInstance()->start(able);
  9. QThreadPool pool;
  10. pool.start(able);
  11. }

  

线程池函数的使用

  1. QThreadPool pool;
  2. qDebug()<<"本机的线程数"<<QThread::idealThreadCount();
  3. qDebug()<<"线程池中的线程数"<<pool.maxThreadCount();
  4. pool.setMaxThreadCount(10);//设置线程池中最大的线程数
  5. pool.setExpiryTimeout(40000);//如果线程40000毫秒,40秒未使用,使它过期
  6. Runnable *runnable=new Runnable;
  7. pool.reserveThread();//保留一个线程
  8. pool.start(runnable);//开启线程
  9. qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();
  10. pool.releaseThread();//释放那个保留的线程
  11. qDebug()<<"当前活跃线程数:"<<pool.activeThreadCount();

    

QRunnable释放内存问题:

QRunnable可以执行完释放内存:需要将autoDelete()设置为true,默认为true。

  1. #ifndef RUNNABLE_H
  2. #define RUNNABLE_H
  3. #include<QThread>
  4. #include<QDebug>
  5. #include<QRunnable>
  6. class Runnable:public QRunnable
  7. {
  8. public:
  9. explicit Runnable()//构造函数
  10. {
  11. }
  12. ~Runnable()//析构函数
  13. {
  14. qDebug()<<__FUNCTION__;
  15. }
  16. void run()//重写run函数
  17. {
  18. qDebug()<<"thread to run():"<<QThread::currentThreadId();
  19. }
  20. };
  21. #endif // RUNNABLE_H
  1. QThreadPool pool;
  2. Runnable *runnable=new Runnable;
  3. pool.start(runnable);//开启线程

执行完后自动调用析构函数。

  

QRunnable的通信问题:

由于QRunnable是单独的一个类,并没有继承QObject,所以没有connect()函数,所以无法于外界进行通信。

解决该问题的方法:

  • 使用QMetaObject::invokeMethod()
  • 使用多继承的方式,同时继承QRunnable和QObject,但这样的话会使接口混乱(尽量不要使用)

QMetaObject::invokeMethod()的创建方法 :

  1. QMetaObject::invokeMethod(QObject *obj, //使用的类
  2. const char *member, //调用函数名
  3. Qt::ConnectionType type//调用类型
  4. QGenericReturnArgument ret,//用来存储返回值的类型
  5. QGenericArgument val0 = QGenericArgument(nullptr),
  6. QGenericArgument val1 = QGenericArgument(),
  7. QGenericArgument val2 = QGenericArgument(),
  8. QGenericArgument val3 = QGenericArgument(),
  9. QGenericArgument val4 = QGenericArgument(),
  10. QGenericArgument val5 = QGenericArgument(),
  11. QGenericArgument val6 = QGenericArgument(),
  12. QGenericArgument val7 = QGenericArgument(),
  13. QGenericArgument val8 = QGenericArgument(),
  14. QGenericArgument val9 = QGenericArgument())
  15. //最多有10个参数

*obj:需要把使用类传进去

member:函数的名称

type:调用类型:

  1. Qt::DirectConnnect 立即调用该成员函数
  2. Qt::QueuedConnection 一旦应用程序进入主事件循环,就会发送 QEvent 并调用成员。
  3. Qt::BlockQueuedConnection 当前线程将阻塞,直到事件被传递。使用此连接类型在同一线程中的对象之间进行通信将导致死锁。
  4. Qt::AutoConnection 如果 obj 与调用方位于同一线程中,则同步调用成员;否则,它将异步调用该成员。

val0-val9:最多可以有10个参数

传参的方式:使用宏传参

  1. Q_ARG(type,const Type&value)
  2. Q_RETURN_ARG(type, Type&value)
  1. QString S="hellow";
  2. QMetaObject::invokeMethod(obj,
  3. "Text",
  4. Qt::DirectConnection,
  5. Q_ARG(QString,"pppp"),
  6. Q_RETURN_ARG(QString,S));

Runnable函数:

  1. #ifndef RUNNABLE_H
  2. #define RUNNABLE_H
  3. #include "widget.h"
  4. #include<QThread>
  5. #include<QObject>
  6. #include<QDebug>
  7. #include<QRunnable>
  8. #include<QMetaObject>
  9. class Runnable:public QRunnable
  10. {
  11. public:
  12. explicit Runnable(QObject *obj1):obj(obj1)//构造函数
  13. {
  14. }
  15. ~Runnable()//析构函数
  16. {
  17. }
  18. void run()//重写run函数
  19. {
  20. QString S="hellow";
  21. QMetaObject::invokeMethod(obj,"Text",Qt::DirectConnection,Q_ARG(QString,"pppp"),Q_RETURN_ARG(QString,S));
  22. qDebug()<<"thread to run():"<<QThread::currentThreadId();
  23. }
  24. QObject *obj=nullptr;//需要传的对象
  25. };
  26. #endif // RUNNABLE_H

主函数:

主类中创建函数开头需要添加:Q_INVOKABLE(这样才直到调用哪一个函数)

  1. #include "widget.h"
  2. #include "ui_widget.h"
  3. #include"runnable.h"
  4. #include<QThreadPool>
  5. class Rnnable;
  6. Widget::Widget(QWidget *parent)
  7. : QWidget(parent)
  8. , ui(new Ui::Widget)
  9. {
  10. ui->setupUi(this);
  11. QThreadPool pool;
  12. Runnable *runnable=new Runnable(this);//传入调用的对象
  13. pool.start(runnable);//开启线程
  14. }
  15. Q_INVOKABLE void Widget::Text(QString a,QString b)//创建一个函数,名称要相同
  16. {
  17. qDebug()<<a;
  18. qDebug()<<b;
  19. }
  20. Widget::~Widget()
  21. {
  22. delete ui;
  23. }

    

Qt Concurrent(并发)

QtConcurrent命名空间提供高级 API,可以在不使用互斥锁、读写锁、等待条件或信号量等低级线程原语的情况下编写多线程程序。

使用 QtConcurrent编写的程序会根据可用的处理器内核数量自动调整使用的线程数。这意味着编写的应用程序在部署到多核系统时将自动扩展。

如何使用Qt Concurrent:

1.pro文件中添加

QT +=concurrent

2. 添加头文件

#include<QtConcurrent>

 以下是如何执行QtConcurrent的使用方法:

使用QtConcurrent::run()函数执行内容

 函数模型:

  1. //T为模板类型
  2. QFuture<T> QtConCurrent::run(Function)//执行一个函数
  3. QFuture<T> QtConCurrent::run(threadpool,Function)//线程池中,取出线程执行function函数

执行一个函数:

  1. void text()
  2. {
  3. qDebug()<<"ok";
  4. }
  5. void text1(int a)
  6. {
  7. qDebug()<<a;
  8. }
  9. void text2(QString s1,QString s2)
  10. {
  11. qDebug()<<s1+s2;
  12. }
  13. int main(int argc, char *argv[])
  14. {
  15. QApplication a(argc, argv);
  16. QFuture<void> fu=QtConcurrent::run(text);
  17. QFuture<void> fu1=QtConcurrent::run(text1,10);
  18. QFuture<void> fu2=QtConcurrent::run(text2,QString("aa"),QString("bb")");
  19. fu.waitForFinished();//等待完成
  20. fu1.waitForFinished();//等待完成
  21. fu2.waitForFinished();//等待完成
  22. return a.exec();
  23. }

   

通过线程池中的进程执行函数

  1. void text1(int a)
  2. {
  3. qDebug()<<a;
  4. }
  5. int main(int argc, char *argv[])
  6. {
  7. QApplication a(argc, argv);
  8. QThreadPool pool;
  9. QFuture<void> fu=QtConcurrent::run(&pool,text1,10);
  10. fu.waitForFinished();//等待完成
  11. return a.exec();
  12. }

  

3.调用常量成员函数:

run()函数可以接收成员函数的指针,第一个参数必须是常量引用或指向类实例的指针。

  1. QByteArray bytearray = "hello world";
  2. QFuture<QList<QByteArray> > future = QtConcurrent::run(bytearray, &QByteArray::split, ',');
  3. QList<QByteArray> result = future.result();

4.调用非常量成员函数:

  1. QImage image = ...;
  2. QFuture<void> future = QtConcurrent::run(&image, &QImage::invertPixels,QImage::InvertRgba);
  3. future.waitForFinished();


总结:以上三种多线程的特点:

特征QThreadQRunnable和QThreadPoolQtConcurrent::run()

QtConcurrent

(Map,Filter,Reduce)

指定优先级

可运行

事件循环

线程可以通过

信号接收数据更新

可以使用

信号控制螺纹

是(QFutureWatcher)

可以通过

QFuture监控线程

部分是

内置暂停/

恢复/取消功能

适用场景:

生命周期开发任务解决方案
持续运行在另一个线程中重复执行昂贵的操作,其中线程不需要接收任何信号或事件。直接在QThread::run()的重新实现中编写无限循环。在没有事件循环的情况下启动线程。让线程发出信号以将数据发送回 GUI 线程。
持续运行让一个对象存在于另一个线程中,该线程可以根据请求执行不同的任务和/或可以接收要使用的新数据。子类 一个QObject 以创建工作线程。实例化此工作线程对象和 QThread。将工作线程移动到新线程。通过排队的信号槽连接将命令或数据发送到工作器对象。
一次调用在另一个线程中运行新的线性函数,可以选择在运行期间更新进度。Qt提供不同的解决方案:
  • 将函数放在 QThread::run() 的重新实现中并启动 Qthread。发出信号以更新进度。或
  • 将函数放在 QRunnable::run() 的重新实现中,并将 QRunnable 添加到 QthreadPool 中。写入线程安全变量以更新进度。或
  • 使用 Q他Concurrent() 运行函数。写入线程安全变量以更新进度。
一次调用在另一个线程中运行现有函数并获取其返回值。使用 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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/104570
推荐阅读
相关标签
  

闽ICP备14008679号