zl程序教程

您现在的位置是:首页 >  工具

当前栏目

QT多线程信号槽使用

Qt多线程 信号 使用
2023-09-14 09:05:12 时间

一、前言

QT的信号槽在多线程环境下经常有人遇到信号槽连接后执行有问题,或者直接不执行,所以新版推荐大家使用QObject::moveToThread方法来实现多线程连接信号槽,实际测试表示这种方法非常实用,但是写的代码就增加一些,还容易弄错,也没有彻底理解为什么,今天就来谈谈并且做一个封装

二、QT多线程信号槽机制

QT的槽函数执行线程决定于宿主对象所在线程而无关信号发出线程,这也是moveToThread的根本所在,就是将宿主对象移到线程内,这样槽函数的执行权才能在线程内。
而有些小伙伴用传统继承QThread的方法,在线程内部创建对象连接信号槽后,连槽函数都没有得到执行,追根溯源是因为虽然宿主对象在线程内,但是线程没有事件循环,而QT的信号槽是依赖事件循环来执行的,这时候就需要在run函数内执行exec函数,便可以开启QThread的事件循环。

三、例子

main 0x6a18
todo start 0x1f00
todo end 0x1f00
todo Print 0x6a18
todo start 0x1f00
todo end 0x1f00
todo Print 0x6a18
todo start 0x1f00
todo end 0x1f00
todo Print 0x6a18
.h
#include "YThreadObject.h"
class Thread : public YThreadObject
{
    Q_OBJECT
public:
    explicit inline Thread(QObject* parent = nullptr): YThreadObject(parent) {}

signals:
    void finished();

public slots:
    void todo();

};
void Thread::todo()
{
    qDebug() << __func__ << "start" << QThread::currentThreadId();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    qDebug() << __func__ << "end" << QThread::currentThreadId();
    emit finished();
}

class Print : public QObject
{
    Q_OBJECT
public:
    explicit inline Print(QObject* parent = nullptr): QObject(parent) {}

public slots:
    void todo();

};
void Print::todo()
{
    qDebug() << __func__ << "Print" << QThread::currentThreadId();
}


.cpp
int main(int argc, char* argv[])
{
    QApplication application(argc, argv);

    Thread thread;
    Print print;
    QTimer timer;

    timer.connect(&timer, &QTimer::timeout, &thread, &Thread::todo);
    timer.connect(&thread, &Thread::finished, &print, &Print::todo);
    timer.connect(&timer, &QTimer::timeout, [&timer]{
        static int index = 0;
        ++index;
        if(index >= 5) timer.stop();
    });
    timer.start(2000);
    qDebug() << __func__ << QThread::currentThreadId();

    return application.exec();
}

四、QObject::moveToThread的封装 

.h
#ifndef YTHREADOBJECT_H
#define YTHREADOBJECT_H

#include <QObject>

class QThread;
class YThreadObjectParasitic;
class YThreadObject : public QObject
{
    Q_OBJECT
public:
    explicit YThreadObject(QObject *parent = nullptr);
    ~YThreadObject();
    bool wait(unsigned long time = ULONG_MAX);

signals:

public slots:

public:
    QThread* thread = nullptr;
    YThreadObjectParasitic* parasitic = nullptr;
};

#endif // YTHREADOBJECT_H


.cpp
#include "YThreadObject.h"
#include <QThread>

class YThreadObjectParasitic : public QObject
{
public:
    explicit YThreadObjectParasitic(QObject *parent, YThreadObject* object) :
        QObject(parent), object(object), currentThreadId(QThread::currentThreadId()){}
    ~YThreadObjectParasitic() {
        if(object){
            YThreadObject* tobject = object;
            object = nullptr;
            delete tobject;
        }
    }
    YThreadObject* object = nullptr;
    Qt::HANDLE currentThreadId;
};

YThreadObject::YThreadObject(QObject *parent) : QObject(nullptr), parasitic(new YThreadObjectParasitic(parent, this))
{
    thread = new QThread;
    moveToThread(thread);
    thread->start();
    connect(thread, &QThread::finished, thread, &QThread::deleteLater);
}

YThreadObject::~YThreadObject()
{
    if(thread){
        thread->quit();
        if(parasitic->currentThreadId == QThread::currentThreadId()){
            thread->wait();
        }
        thread = nullptr;
    }
    if(parasitic->object){
        parasitic->setParent(nullptr);
        parasitic->object = nullptr;
        parasitic->deleteLater();
        parasitic = nullptr;
    }
}

bool YThreadObject::wait(unsigned long time)
{
    if(thread){
        return thread->wait(time);
    }
    return true;
}

QT技术文档推荐:Qt开发必备技术栈学习路线和资料