目录
一、继承QThread
1.基本概念
2.操作流程
二、继承QObject(推荐)
1.基本概念
2.操作流程
三、继承QRunnable,配合QThreadPool实现多线程
1.外界通信
2.QMetaObject::invokeMethod()介绍
3.QMetaObject::invokeMethod()使用方式
四、使用QtConcurrent::run()
1.基本概念
2.操作流程
3.实现案例
五,线程同步
1.低级同步原语
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();
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中多线程的使用 | 爱编程的大丙
使用信号槽通信方式可以查看这篇文章 Qt中线程池的使用 | 爱编程的大丙
Qt::DirectConnection |
该成员将立即被调用,同步调用 |
Qt::QueuedConnection |
一旦应用程序进入主事件循环,就会发送一个QEvent并调用成员,异步调用 |
Qt::BlockingQueuedConnection |
该方法将以与Qt::QueuedConnection相同的方式被调用,除了当前线程将阻塞,直到事件被传递。使用这种连接类型在同一线程中的对象之间通信将导致死锁。 |
Qt::AutoConnection |
如果obj与调用者位于同一个线程中,则同步调用成员;否则,它将异步调用成员。 |
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的使用详解
Qt Concurrent模块包含支持程序代码并发执行的功能,可以在不使用低级线程原语的情况下编写多线程程序,它是一个单独的模块,使用起来也非常简单,不用向上面三种那样去为了某个业务处理逻辑而编写线程类,这种方式只需要你把耗时的某个接口传入该接口就行,具有用法看下面
1.在.pro文件中添加QT += concurrent
2.然后在耗时接口所在的头文件中包含QtConcurrent,并在所在源文件中调用QtConcurrent::run()
QtConcurrent::run(this,&QtConcurrentDemo::working);
1.界面搭建,红色注释为个控件对象名称
2.头文件如下
#ifndef QTCONCURRENTDEMO_H
#define QTCONCURRENTDEMO_H
#include
#include
#include
#define cout qDebug() << "[" << __FUNCTION__ << "][" << __LINE__ << "]:"
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 在子线程中运行
cout<startNotThread,&QPushButton::clicked,this,[=](){
//耗时接口 working 在主线程中运行
cout<closeThread,&QPushButton::clicked,this,[=](){
//关闭线程
cout<label->setNum(0);
});
}
QtConcurrentDemo::~QtConcurrentDemo()
{
delete ui;
}
void QtConcurrentDemo::working()
{
cout<label->setNum(i++);
cout<
虽然线程的目的是允许代码并行运行,但有时线程必须停止并等待其他线程。例如,如果两个线程试图同时写入同一个变量,结果是未定义的。强迫线程相互等待的原则称为互斥。这是保护数据等共享资源的常用技术。
Qt提供了用于同步线程的低级原语和高级机制,介绍如下:
注意:Qt的这些同步类依赖于使用正确对齐的指针。例如,不能在MSVC中使用打包类。
这些同步类可用于使方法线程安全。然而,这样做会导致性能损失,这就是为什么大多数Qt方法不是线程安全的。
风险:
方便类: