目录
一、继承QThread
1,基本概念
2,操作流程
二、继承QObject(推荐)
1,基本概念
2,操作流程
三、继承QRunnable,配合QThreadPool实现多线程
1,外界通信
2,QMetaObject::invokeMethod()介绍
3,QMetaObject::invokeMethod()使用方式
四、使用QtConcurrent::run()
1,基本概念
2,操作流程
3,实现案例
一、继承QThread
1,基本概念
- 一个QThread类的对象管理一个子线程,自定义一个类继承自QThread,并重写虚函数run(),在run()函数里实现线程需要完成的复杂操作(注意QThread只有run函数是在新线程里的)。
- 一般在主线程创建工作子线程,并调用start(),开始执行工作子线程的任务。start()会在内部调用run()函数,进入工作线程的事件循环,在run()函数里调用exit()或quit()可以结束线程的事件循环,或者在工作主线程里调用terminate()强制结束线程。
2,操作流程
1.创建一个继承QThread线程类的子类,记得包含头文件QThread
class subThread : public QThread
{
...
};
2.重写父类的虚函数run()方法,在该方法内部实现子线程需要完成的复杂业务
class subThread : public QThread
{
...
protected:
void run(){
//全部在这里处理子线程的复杂业务
}
};
3.在主线程中创建子线程对象
subThread* st = new subThread;
4.启动子线程,调用start()方法
st->start();
二、继承QObject(推荐)
1,基本概念
- 如果有编写多个业务类,各不相关的业务逻辑需要被处理,就可以选择这种方式,将业务逻辑放入对应业务类的公共函数中,然后将这些业务类的实例对象移动到对应的子线程中moveToThread()就可以了,这种编写多线程的方式比第一种更加灵活,可读性也更强,更易于维护
2,操作流程
1.创建一个继承QObject类的子类,记得包含头文件QObject
class subObject : public QObject
{
...
}
2.在该类中添加一个公共的成员函数,主要负责子线程中要执行的业务逻辑
class subObject : public QObject
{
...
public:
void working(); //函数名称随意取,传入的参数根据实际需求添加
}
3.在主线程中创建一个QThread对象,也就是子线程的对象
QThread* subThread = new QThread;
4.主线程中创建该类的对象(创建该类对象千万不要指定父对象)
subObject* subObj = new subObject(this); //error
subObject* subObj = new subObject; //OK
5.将工作类subObject对象移动到创建的子线程对象中,需要调用继承自QObject类提供的 moveToThread()方法
// void moveToThread(QThread *targetThread)
subObj->moveToThread(subThread);
6. 启动子线程,调用start()方法,但这时候并不会启动子线程的工作函数
subThread->start();
7.在主线程中通过信号槽调用线程类subObject对象的工作函数,这时候才会到子线程中运行该工作函数
connect(ui->pushButton,&QPushButton::clicked,subObj,&subObject::working);
参考文章 Qt中多线程的使用 | 爱编程的大丙
三、继承QRunnable,配合QThreadPool实现多线程
使用信号槽通信方式可以查看这篇文章 Qt中线程池的使用 | 爱编程的大丙
1,外界通信
- QRunnable类是所有可运行对象的基类,没有继承于QObject,所以就不能使用信号槽功能与外界通信
- 使用多继承,就是让线程类同时继承QObject和QRunnable(上述文章就使用这种方式,但不推荐,毕竟C#都是单继承的,不多解释啦),让该线程类能够支持信号槽的使用
- 使用QMetaObject::invokeMethod()
2,QMetaObject::invokeMethod()介绍
- 该静态函数调用 obj 对象上的成员如信号,槽名,Q_INVOKABLE声明的函数(能够被Qt元对象系统唤起,所以传入的第一个对象指针是QObject类型或QObject子类类型)。如果成员可以被调用,则返回true。如果没有这样的成员或形参不匹配,则返回false。
- 调用可以是同步的,也可以是异步的,这取决于第三个参数类型ConnectionType:
Qt::DirectConnection | 该成员将立即被调用,同步调用 |
Qt::QueuedConnection | 一旦应用程序进入主事件循环,就会发送一个QEvent并调用成员,异步调用 |
Qt::BlockingQueuedConnection | 该方法将以与Qt::QueuedConnection相同的方式被调用,除了当前线程将阻塞,直到事件被传递。使用这种连接类型在同一线程中的对象之间通信将导致死锁。 |
Qt::AutoConnection | 如果obj与调用者位于同一个线程中,则同步调用成员;否则,它将异步调用成员。 |
- 成员函数调用的返回值放在ret中,如果调用是异步的,则无法计算返回值。您最多可以向成员函数传递十个参数。
- QGenericArgument和QGenericReturnArgument是内部的辅助类。因为信号和槽可以动态调用,所以必须使用Q_ARG()和Q_RETURN_ARG()宏将参数括起来。Q_ARG()接受类型名和该类型的const引用,Q_RETURN_ARG()接受一个类型名和一个非const引用。
3,QMetaObject::invokeMethod()使用方式
1.线程类中通信的函数
Q_INVOKABLE void externalTonXin(QString info);
2.在重写虚函数run内加入该接口调用方式,参数m_obj为主线程对象指针,"externalTonXin"是要调用被Q_INVOKABLE声明的函数
QMetaObject::invokeMethod(m_obj,"externalTonXin",Q_ARG(QString,"子线程主线程通信..."));
3.在主线程创建线程对象时,需要将主界面对象(this)传入线程对象构造函数中
RunnableThread* st = new RunnableThread(this);
参考文章 Qt线程之QRunnable的使用详解
四、使用QtConcurrent::run()
1,基本概念
Qt Concurrent模块包含支持程序代码并发执行的功能,可以在不使用低级线程原语的情况下编写多线程程序,它是一个单独的模块,使用起来也非常简单,不用向上面三种那样去为了某个业务处理逻辑而编写线程类,这种方式只需要你把耗时的某个接口传入该接口就行,具有用法看下面
2,操作流程
1.在.pro文件中添加QT += concurrent
2.然后在耗时接口所在的头文件中包含QtConcurrent,并在所在源文件中调用QtConcurrent::run()
QtConcurrent::run(this,&QtConcurrentDemo::working);
3,实现案例
1.界面搭建,红色注释为个控件对象名称
2.头文件如下
#ifndef QTCONCURRENTDEMO_H
#define QTCONCURRENTDEMO_H
#include <QWidget>
#include <QDebug>
#include <QtConcurrent>
namespace Ui {
class QtConcurrentDemo;
}
class QtConcurrentDemo : public QWidget
{
Q_OBJECT
public:
explicit QtConcurrentDemo(QWidget *parent = nullptr);
~QtConcurrentDemo();
void working(); //耗时接口,主线程中运行会阻塞,子线程中能正常运行
private:
Ui::QtConcurrentDemo *ui;
bool flag; //控制线程开闭
};
#endif // QTCONCURRENTDEMO_H
3.源文件如下
#include "qtconcurrentdemo.h"
#include "ui_qtconcurrentdemo.h"
QtConcurrentDemo::QtConcurrentDemo(QWidget *parent) :
QWidget(parent),
ui(new Ui::QtConcurrentDemo)
{
ui->setupUi(this);
flag = false;
connect(ui->startThread,&QPushButton::clicked,this,[=](){
//耗时接口 working 在子线程中运行
qDebug()<<__FUNCTION__<<QThread::currentThreadId();
flag = true;
QtConcurrent::run(this,&QtConcurrentDemo::working);
});
connect(ui->startNotThread,&QPushButton::clicked,this,[=](){
//耗时接口 working 在主线程中运行
qDebug()<<__FUNCTION__<<QThread::currentThreadId();
flag = true;
working();
});
connect(ui->closeThread,&QPushButton::clicked,this,[=](){
//关闭线程
qDebug()<<__FUNCTION__<<QThread::currentThreadId();
flag = false;
ui->label->setNum(0);
});
}
QtConcurrentDemo::~QtConcurrentDemo()
{
delete ui;
}
void QtConcurrentDemo::working()
{
qDebug()<<__FUNCTION__<<QThread::currentThreadId();
int i=0;
while (flag) {
ui->label->setNum(i++);
qDebug()<<__FUNCTION__<<i;
QThread::sleep(1);
}
}