Qt实现多线程的两种方式:重写run()和moveToThread(),connect第五个参数说明

重写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);

猜你喜欢

转载自blog.csdn.net/weixin_40355471/article/details/110393257
今日推荐