重写run():定义一个线程类,继承与QThread,重写虚函数run(),run里的代码是在子线程执行;
moveToThread():定义一个线程类,继承于QObject,在主线程实例化一个该类的对象,并实例化一个QThread类的对象,前者moveToThread后者,通过信号槽的方式实现多线程,可以定义多个信号槽,槽函数都是在同一个子线程运行,可以通过connect第五个参数控制槽函数是否在子线程运行(多线程下默认是多线程,队列是多线程,直接是在主线程运行槽)。
重写QThread的虚函数run()
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QThread>
//头文件和基类由QObject改成QThread
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
protected:
//QThread的虚函数
//线程处理函数
//不能直接调用,通过start()间接调用
void run();
signals:
void isDone();
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
//基类改成QThread
MyThread::MyT hread(QObject *parent) : QThread(parent)
{
}
void MyThread::run()
{
//非常复杂的数据处理
//耗时5s
sleep(5);
emit isDone();
}
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#ifdef WIN32
#pragma execution_character_set("utf-8")
#endif
#include <QWidget>
#include<QTimer>//定时器头文件
#include"mythread.h"//线程头文件
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
void dealTimerout();//定时器槽函数
void dealDone();//线程结束槽函数
void stopThread();//停止线程槽函数
private slots:
void on_pushButton_clicked();
private:
Ui::MyWidget *ui;
QTimer *myTimer;//声明变量
MyThread *thread;//线程对象
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QThread>
#include<QDebug>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
myTimer = new QTimer(this);
//只要定时器启动,自动触发timeout()槽函数
connect(myTimer,&QTimer::timeout,this,&MyWidget::dealTimerout);
thread = new MyThread(this);
connect(thread,&MyThread::isDone,this,&MyWidget::dealDone);
//当按窗口右上角关闭时,窗口触发destroy()信号
connect(this,&MyWidget::destroyed,this,&MyWidget::stopThread);
}
//停止线程
void MyWidget::stopThread()
{
//停止线程
thread->quit();
//等待线程处理完手头工作
thread->wait();
}
void MyWidget::dealDone()
{
qDebug() << "it is over";
myTimer->stop();//关闭定时器
}
void MyWidget::dealTimerout()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
MyWidget::~MyWidget()
{
delete ui;
}
//启动线程
void MyWidget::on_pushButton_clicked()
{
//如果定时器没有工作,点start才进入
if(myTimer->isActive() == false)
{
myTimer->start(100);
}
//启动线程,处理数据
thread->start();
}
moveToThread()
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#ifdef WIN32
#pragma execution_character_set("utf-8")
#endif
#include <QObject>
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
//线程处理函数
void myTimeout();
void setFlag(bool flag = true);
signals:
void mySignal();
private:
bool isStop;
public slots:
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include<QThread>
#include<QDebug>
MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop = false;
}
//线程处理函数
void MyThread::myTimeout()
{
while(isStop == false)
{
//每隔1s发射一个信号
QThread::sleep(1);
emit mySignal();
//打印子线程号
qDebug() << "子线程号:" << QThread::currentThread();
if(isStop == true)
break;
}
}
void MyThread::setFlag(bool flag)
{
isStop = flag;
}
mywidget.h
#ifndef MYWIDGET_H
#define MYWIDGET_H
#ifdef WIN32
#pragma execution_character_set("utf-8")
#endif
#include <QWidget>
#include"mythread.h"
#include<QThread>
namespace Ui {
class MyWidget;
}
class MyWidget : public QWidget
{
Q_OBJECT
public:
explicit MyWidget(QWidget *parent = nullptr);
~MyWidget();
void dealSignal();
void dealClose();
signals:
void startThread();//启动子线程的信号
private slots:
void on_buttonStart_clicked();
void on_buttonStop_clicked();
private:
Ui::MyWidget *ui;
MyThread *myT;
QThread *thread;
};
#endif // MYWIDGET_H
mywidget.cpp
#include "mywidget.h"
#include "ui_mywidget.h"
#include<QDebug>
MyWidget::MyWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyWidget)
{
ui->setupUi(this);
//动态分配空间,不能指定父对象,因为下一步要加入到子线程
myT = new MyThread;
//创建子线程,可以指定父对象
thread = new QThread(this);
//把自定义的线程加入到子线程中
myT->moveToThread(thread);
connect(myT,&MyThread::mySignal,this,&MyWidget::dealSignal);
//打印主线程号
qDebug() << "主线程号:" << QThread::currentThread();
connect(this,&MyWidget::startThread,myT,&MyThread::myTimeout);
//右上角x关闭窗口时,关闭线程
connect(this,&MyWidget::destroyed,this,&MyWidget::dealClose);
//线程处理函数内部,不允许操作图形界面
//connect()第五个参数的作用,连接方式:默认、队列、直接
//第五个参数多线程时才有意义
//默认的时候
//如果是多线程,默认使用队列
//如果是单线程,默认使用直接方式
//队列:槽函数所在线程和接收者一样
//直接:槽函数所在线程和发送者一样
//一般选用默认,程序会自动判断单线程还是多线程
}
MyWidget::~MyWidget()
{
delete ui;
}
void MyWidget::dealClose()
{
// myT->setFlag(true);
// thread->quit();
// thread->wait();
//或者直接调用槽函数
on_buttonStop_clicked();
//myT未指定父对象,关闭时释放内存
delete myT;
}
void MyWidget::dealSignal()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
void MyWidget::on_buttonStart_clicked()
{
//做个判断,线程已启动就不进入
if(thread->isRunning() == true)
return;
//启动线程,但是没有启动线程处理函数
thread->start();
myT->setFlag(false);
//不能直接调用线程处理函数
//直接调用会导致,线程处理函数和主线程是在同一个线程
//myT->myTimeout(); //运行异常,主线程和子线程号一样
//只能通过 signal - slot 方式调用(信号与槽)
emit startThread();
}
void MyWidget::on_buttonStop_clicked()
{
//做个判断,线程已关闭,不进行退出操作
if(thread->isRunning() == false)
{
return;
}
//按下stop,Flag变为true
myT->setFlag(true);
thread->quit();
thread->wait();
}
connect第五个参数
Qt::AutoConnection,默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
Qt::DirectConnection,槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection,槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection,槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection,这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。
断开连接的方法:该方法虽然不是必须使用的,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
disconnect(sender,SIGNAL(signal),receiver,SLOT(slot),Qt::DirectConnection);