QT 异步编程之多线程
一、概述
1、在进行桌面应用程序开发的时候,假设应用程序在某些情况下需要处理比较复制的逻辑,如果只有一个线程去处理,就会导致窗口卡顿,无法处理用户的相关操作。这种情况下就需要使用多线程,其中一个线程处理窗口事件,其它线程进行逻辑运算,多个线程各司其职,不仅可以提高用户体验还可以提升程序的执行效率。
2、当前的主线程不能让它做非常复制的逻辑操作,如果你让它做了非常复杂的逻辑操作后,咱们再操作这个窗口的时候就会出现界面的卡顿,那么这些复杂的操作应该交给子线程去做。
3、默认的线程在Qt中称之为窗口线程,也叫主线程,负责窗口事件处理或者窗口控件数据的更新。
4、子线程复制后台的业务逻辑处理,子线程中不能对窗口对象做任何操作,这些事情需要交给窗口线程处理。
5、主线程和子线程之间如果要进行数据的传递,需要使用Qt中的信号与槽机制
二、多线程的使用方式一
1、需要创建一个线程的子类,让其继承QT中的线程类QThread
class MyThread : public QThread
{
...............
}
2、重写父类的run方法,在该函数内部编写子线程要处理的具体的业务流程
class MyThread : public QThread
{
.........
protected:
void run()
{
........
}
}
3、在主线程中创建子线程对象,new一个就可以了
MyThread *subThread = new MyThread;
4、启动子线程,调用start()方法
subThread->start();
5、示例 MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
void print();
protected:
void run() override;
signals:
};
#endif // MYTHREAD_H
6、 示例 MyThread.cpp
#include "mythread.h"
#include <QDebug>
MyThread::MyThread()
{
}
void MyThread::print()
{
qDebug() << "子线程成员函数的线程ID:" << QThread::currentThreadId();
}
void MyThread::run()
{
qDebug() << "子线程的线程ID:" << QThread::currentThreadId();
}
7、main函数
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyThread w;
w.start();
qDebug() << "主线程ID:" << QThread::currentThreadId();
w.print();
return a.exec();
}
8、结果展示
9、结果分析:
(1)主线程和子线程的顺序不确定,偶尔主线程在前,偶尔子线程在前
(2)子线程类的成员函数包括槽函数是运行在主线程当中的,只有run()函数运行在子线程中
(3)如果run()函数中调用子线程的成员函数,那么该成员函数运行在子线程中
二、多线程的使用方式二
1、创建一个新的类,让这个类从QObject派生
class MyWork : public QObject
{
.........
}
2、在这个类中添加一个公共的成员函数,函数体就是我们要子线程中要执行的业务逻辑
class MyWork : public QObject
{
public:
......
//函数名自己指定,叫什么都可以
void working();
}
3、在主线程中创建一个QThread对象,这就是子线程的对象
QThread *sub = new QThread;
4、在主线程中创建工作的类对象(不要给创建的对象指定父对象)
MyWork *work = new MyWork(this); //error
MyWork *work = new MyWork; //ok
5、将MyWork对象移动到创建的子线程对象中,需要调用QObject类提供的moveToThread()方法
work->moveToThread(sub);
6、启动子线程,调用start(),这时候线程启动了,但是移动到线程中的对象并没有工作
7、调用MyWork类对象的工作函数,让这个函数开始执行,这时候是在移动到的那个子线程中运行的
8、示例 MyWork.h
#ifndef MYWORK_H
#define MYWORK_H
#include <QObject>
class MyWork : public QObject
{
Q_OBJECT
public:
explicit MyWork(QObject *parent = nullptr);
void print();
public slots:
void doWork();
signals:
};
#endif // MYWORK_H
9、示例 MyWork.cpp
#include "mywork.h"
#include <QDebug>
#include <QThread>
MyWork::MyWork(QObject *parent)
: QObject{parent}
{
}
void MyWork::print()
{
qDebug() << "成员函数ThreadID:" << QThread::currentThreadId();
}
void MyWork::doWork()
{
qDebug() << "doWork ThreadId:" << QThread::currentThreadId();
}
10、示例 main
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QThread *thread = new QThread();
MyWork *worker = new MyWork;
worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, worker, &MyWork::doWork);
QObject::connect(thread, &QThread::started, worker, &MyWork::print);
qDebug() << "主线程ID:" << QThread::currentThreadId();
//启动线程
thread->start();
//调用成员函数
worker->print();
return a.exec();
}
11、运行结果
22:38:21: Starting D:\QT\practice\mouseTrack\bin\debug\untitled.exe...
主线程ID: 0x56f0
成员函数ThreadID: 0x56f0
doWork ThreadId: 0x49d4
成员函数ThreadID: 0x49d4
12、结果分析
(1)通过信号与槽触发的成员函数、槽函数,执行都在新线程里面,通过对象调用执行,则在旧线程里面
三、两种方式的使用场景
1、QThread使用场景
(1)当需要创建一个独立的线程来执行某个任务,且需要对线程的整个生命周期进行管理时,适合使用 QThread 方式。
(2)当任务逻辑相对简单或独立,不需要频繁地进行线程间通信时,可以选择使用 QThread 方式。
2、moveToThread方式
(1)当需要将一个 QObject 对象移动到指定的线程中执行任务,或者需要多个对象在同一线程中协同工作时,适合使用 moveToThread() 方式。
(2)当需要灵活地控制对象和线程之间的关系,进行复杂的线程间通信时,可以选择使用 moveToThread() 方式。