在QT中多线程的使用方法一共有两种:
先说第一种,继承自QThread类,然后重写虚函数run(),将耗时的操作写到run函数中,从而实现多线程操作,最后只需要在主函数中使用线程的start()函数将线程开启即可,使用起来比较简单,需要注意的是,开启线程时不需要调用run函数,使用线程的start函数即可。这种方法在新建类的时候需要继承自QThread,操作如下图所示。首先点击QT的添加文件按钮,弹出如下界面,选择c++,在右边选择C++ Class,然后点击右下角的Choose。
在弹出的界面中填写类的名字,此处我写的是firstmethod,最重要的是"Base class"这个地方,选择< Custom >,然后在下面的文本框内填写QThread,注意这里不要写错,要跟头文件内的QThread保持大小写一致才可以。
对于此类方法非常简单,此处不通过代码进行详细的说明。
对于moveToThread方法的实现是本文的重点,首先我们需要理解moveToThread的工作原理,这样才能够正确的使用多线程,否则程序虽然跑起来了,但很可能多线程并没有起到作用。
moveToThread形式的多线程实现方法需要将耗时操作实例化为槽函数,将这个槽函数所在的类推入Thread
主线程中调用槽函数实现多线程调度
对,就是上面这段话,如果理解错了,很可能多线程就起不到作用,导致看似是主线程在进行任务调度,实际上子线程没有起到作用。
目前网上有许多的说法,都指向了moveToThread方式的多线程,其实使用该方法时,QT新建类可以继承于QObject也可以继承于QThread,至少我是这么认为的,在本文针对上述我提到的两种方法我也做了实现,感觉没什么问题,继承于QThread时自己新建一个槽函数而不重写run方法,同样可以使用该多线程方式。
首先我把代码的目录结构放一下
这里没有用到界面,所以form里面的UI文件没有做任何修改,直接是新建项目时选择QWidget的默认界面,.pro文件同样没有做任何修改,直接默认即可,这里就不贴出此部分的代码浪费空间了。
然后下面是项目整体的头文件与cpp文件,代码的第一行注释为对应的文件名字
// mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
#include <QString>
#include <QDateTime>
#include <QDebug>
#include <QTime>
/*
说明:耗时的操作放在此类中执行
原因:moveToThread形式的多线程实现方法需要将耗时操作实例化为槽函数,将这个槽函数所在的类推入Thread
主线程中调用槽函数实现多线程调度
*/
class MyThread : public QThread
{
public:
MyThread();
public slots:
void mySlots();
};
#endif // MYTHREAD_H
// mythread.cpp
#include "mythread.h"
MyThread::MyThread()
{
}
void MyThread::mySlots()
{
// qDebug()<<"child thread ID:" << QThread::currentThread()<<"current datetime:" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
// 此处写while的原因是表示较为耗时的操作
while(1){
qDebug()<<"while true!";
qDebug()<<"child thread ID:" << QThread::currentThread();
sleep(1);
}
}
// sencondmethod.h ,此处新建文件时拼错了,多打了一个n,本想写secondmethod,新建文件时可以修正,后面引用头文件和实例化对象统一修改即可
#ifndef SENCONDMETHOD_H
#define SENCONDMETHOD_H
#include <QObject>
#include <QDebug>
#include <QThread>
#include <QDateTime>
#include <QTime>
class sencondmethod : public QObject
{
Q_OBJECT
public:
explicit sencondmethod(QObject *parent = nullptr);
signals:
public slots:
void qDebugDateTime();
};
#endif // SENCONDMETHOD_H
// sencondmethod.cpp
#include "sencondmethod.h"
sencondmethod::sencondmethod(QObject *parent) : QObject(parent)
{
}
void sencondmethod::qDebugDateTime()
{
// qDebug()<<"secondmethod ID:"<<QThread::currentThread()<<"current datetime:"<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
while(1){
qDebug()<<"second while true!";
qDebug()<<"child thread ID:" << QThread::currentThread();
QThread::sleep(1);
}
}
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include "mythread.h"
#include "sencondmethod.h"
#include <QWidget>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
// 定时器,主要是给各个线程定时用的
QTimer *childThreadTimer;
QTimer *parentThreadTimer;
QTimer *secondmethodTimer;
MyThread *mythread;
QThread *childthread;
sencondmethod *second;
QThread *secondthread;
public slots:
void printTime();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// 实例化三个定时器
childThreadTimer = new QTimer(this);
parentThreadTimer = new QTimer(this);
secondmethodTimer = new QTimer(this);
// 主线程操作,定时1秒指定槽函数printTime
connect(parentThreadTimer, &QTimer::timeout, this, &Widget::printTime);
parentThreadTimer->start(1000);
// 首先利用QThread类实例化一个对象
childthread = new QThread();
// 开启线程对象
childthread->start();
// 将自己的类进行实例化,这个类继承自QThread
mythread = new MyThread();
// 将槽函数所在的类推入到Thread中
mythread->moveToThread(childthread);
// 连接槽函数,使用定时器进行操作
connect(childThreadTimer,&QTimer::timeout, mythread, &MyThread::mySlots);
childThreadTimer->start(1000);
// 将自己的类进行实例化,这个类继承自QObject
secondthread = new QThread();
secondthread->start();
second = new sencondmethod();
second->moveToThread(secondthread);
connect(secondmethodTimer, &QTimer::timeout,second, &sencondmethod::qDebugDateTime);
secondmethodTimer->start(1000);
}
Widget::~Widget()
{
delete ui;
}
void Widget::printTime()
{
qDebug()<<"parent thread ID:" << QThread::currentThread()<<"current time" << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
}
// main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
上述操作执行时打印结果如下:
while true!
second while true!
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:08"
child thread ID: QThread(0x1f6241d0)
child thread ID: QThread(0x1f6240b0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:09"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:10"
while true!
second while true!
child thread ID: QThread(0x1f6241d0)
child thread ID: QThread(0x1f6240b0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:11"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:12"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:13"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:14"
while true!
second while true!
child thread ID: QThread(0x1f6241d0)
child thread ID: QThread(0x1f6240b0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:15"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:16"
while true!
second while true!
child thread ID: QThread(0x1f6241d0)
child thread ID: QThread(0x1f6240b0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:17"
second while true!
while true!
child thread ID: QThread(0x1f6240b0)
child thread ID: QThread(0x1f6241d0)
parent thread ID: QThread(0x1f726ce0) current time "2022-07-11 10:30:18"
while true!
second while true!
child thread ID: QThread(0x1f6241d0)
child thread ID: QThread(0x1f6240b0)
从结果中可以看到,程序一共开启了三个线程,每个线程都有自己独立的ID以区别于其它线程,两个子线程的while循环中我分别加入了一个休眠函数,以防止打印太快难以看清楚每一个线程调用操作,实验时为了可以查看每一个线程的操作,可以在子线程的while中加上打印当前时间的操作,同时也可以通过修改线程休眠的时间查看线程的执行结果。
之前的博客中对moveToThread方式的多线程有错误的理解,将定时器与类分别推入到了Thread中,其结果导致了看似线程在运行,而可能是掩盖迷障的结果,因此特别的将之前存在的问题进行修正,同时将正确的使用方式进行分享。
- 我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿。