(Qt) QThread 信号槽所在线程

文章目录

💁🏻前言

在 Qt 中用一套自己的封装的多线程操作。

其中一种方式是继承 QThread 并实现 void run(); 方法使用 void start(); 启动,这是一种很常见的线程的封装方式。这种方式在 java 中也是这么设计的。

但是由于 Qt 的高度封装性和框架整体性,很多特性都是开发者自己测试出来的。

其中 QThread 配合信号槽的特性就是本文要观察的重点。

💁🏻Code

这里放一份基本的code,后面的观察都是基于具体 void QThread::run(); 的修改的观察。

本文下面默认的外部线程值得就是在 main() 中一致的线程。

💁🏻‍♂️Code

目录

bash 复制代码
C:.
└─mythread
        main.cpp
        mythread.cpp
        mythread.h
        mythread.pro

mythread.pro

bash 复制代码
QT = core

CONFIG += c++17 cmdline

SOURCES +=      \
    main.cpp    \
    mythread.cpp

HEADERS += \
    mythread.h

main.cpp

只负责创建我们观察的实例

cpp 复制代码
#include <QCoreApplication>
#include <QDebug>
#include <QThread>

#include "mythread.h"

int main(int argc, char* argv[]) {
    QCoreApplication a(argc, argv);
    qDebug() << "Main Thread" << QThread::currentThread();

    MyThread myth;
    qDebug() << "Object-Thread" << myth.thread();

    return a.exec();
}

mythread.h

cpp 复制代码
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>

class MyThread : public QThread {
    Q_OBJECT
public:
    explicit MyThread(QObject *parent = nullptr);

protected:
    void run() override;

signals:
    void signal_test();
};

#endif  // MYTHREAD_H

mythread.cpp

在构造中就启动线程。(主要是想把所有操作都几种在一个文件中,不影响测试的效果个逻辑)

在槽函数的事件中,再次发出信号,查看连续的信号槽的效果。

cpp 复制代码
#include "mythread.h"

#include <QDebug>
#include <QThread>
#include <QTimer>

MyThread::MyThread(QObject *parent) : QThread{parent} {
    qDebug() << QString("Construction-Line[%1]").arg(__LINE__) << QThread::currentThread();
    start();

    // QTimer::singleShot(100, this, [this]() { emit signal_test(); });
}

void MyThread::run() {
    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();

    auto task = [this]() {
        qDebug() << QString(">>>Line[%1]").arg(__LINE__) << QThread::currentThread();

        QThread::sleep(1);
        emit signal_test();
    };

    connect(this, &MyThread::signal_test, task);
    // emit signal_test();

    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();
    exec();
}

当前模板下的测试样例效果:

由于这是一个不会自己终止的程序,因此下文中打印的效果仅截取关键部分表示示意。

bash 复制代码
Main Thread QThread(0x11072c8)
"Construction-Line[8]" QThread(0x11072c8)
Object-Thread QThread(0x11072c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)

💁🏻‍♂️环境

本文测试环境:Qt 5.15.2 MinGW 32-bit

下面放两个版本的 connect 的实现方式。

cpp 复制代码
// 该 connect 的实现如下
connect(this, &MyThread::signal_test, task);

// Qt 5.15.2
//connect to a functor
template <typename Func1, typename Func2>
static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type
        connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
{
    return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection);
}

// Qt 6.7.2
inline QMetaObject::Connection QObject::connect(const QObject *asender, const char *asignal,
                                            const char *amember, Qt::ConnectionType atype) const
{ return connect(asender, asignal, this, amember, atype); }

💁🏻当前线程信号

解开 void run(); 函数中的信号发送。

效果

根据不同的 connect,不只是最后一个连接类型 ConnectionType

槽函数可能在子线程,可能在主线程执行。

💁🏻‍♂️默认效果

cpp 复制代码
void MyThread::run() {
    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();

    auto task = [this]() {
        qDebug() << QString(">>>Line[%1]").arg(__LINE__) << QThread::currentThread();

        QThread::sleep(1);
        emit signal_test();
    };

    connect(this, &MyThread::signal_test, task);
    emit signal_test();

    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();
    exec();
}
bash 复制代码
Main Thread QThread(0x12372c8)
"Construction-Line[8]" QThread(0x12372c8)
Object-Thread QThread(0x12372c8)
"Run-Line[15]" MyThread(0x76fe70)
">>>Line[18]" MyThread(0x76fe70)
">>>Line[18]" MyThread(0x76fe70)

效果

直接在当前线程中,发送信号,并默认的绑定 connect 是直接执行槽函数的,并槽函数执行线程是子线程。

并且由于在槽函数继续发送信号,导致下面的 qDebug() 没有打印。

💁🏻‍♂️Qt::ConnectionType::AutoConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::AutoConnection);
bash 复制代码
Main Thread QThread(0x11b72c8)
"Construction-Line[8]" QThread(0x11b72c8)
Object-Thread QThread(0x11b72c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0x11b72c8)
">>>Line[18]" QThread(0x11b72c8)

效果

执行到 exec(), 槽函数在主线程执行。

效果同 Qt::ConnectionType::QueuedConnection

💁🏻‍♂️Qt::ConnectionType::DirectConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::DirectConnection);
bash 复制代码
Main Thread QThread(0x9372c8)
"Construction-Line[8]" QThread(0x9372c8)
Object-Thread QThread(0x9372c8)
"Run-Line[15]" MyThread(0x76fe70)
">>>Line[18]" MyThread(0x76fe70)
">>>Line[18]" MyThread(0x76fe70)

效果

在执行 exec() 前就触发槽函数。即槽函数在子线程中运行。

💁🏻‍♂️Qt::ConnectionType::QueuedConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::QueuedConnection);
bash 复制代码
Main Thread QThread(0x11e72c8)
"Construction-Line[8]" QThread(0x11e72c8)
Object-Thread QThread(0x11e72c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0x11e72c8)
">>>Line[18]" QThread(0x11e72c8)

效果

执行到 exec(), 槽函数在主线程执行。

💁🏻外部线程的信号

在构造中直接启动线程,并在构造的线程中发送一个信号。

为什么要加一个定时器,因为要等待事件循环(Qt基础,不过多解释)。

cpp 复制代码
MyThread::MyThread(QObject *parent) : QThread{parent} {
    qDebug() << QString("Construction-Line[%1]").arg(__LINE__) << QThread::currentThread();
    start();

    QTimer::singleShot(100, this, [this]() { emit signal_test(); });
}

效果

下方测试:均执行到 exec() 槽函数在主线程执行。

💁🏻‍♂️默认效果

cpp 复制代码
void MyThread::run() {
    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();

    auto task = [this]() {
        qDebug() << QString(">>>Line[%1]").arg(__LINE__) << QThread::currentThread();

        QThread::sleep(1);
        emit signal_test();
    };

    connect(this, &MyThread::signal_test, task);
    // emit signal_test();

    qDebug() << QString("Run-Line[%1]").arg(__LINE__) << QThread::currentThread();
    exec();
}
bash 复制代码
Main Thread QThread(0xa772c8)
"Construction-Line[8]" QThread(0xa772c8)
Object-Thread QThread(0xa772c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0xa772c8)
">>>Line[18]" QThread(0xa772c8)

效果

首先,这里会保证 void run(); 可以执行到 void exec(); 所以在 exec 上面的所有代码都可以执行。

但由于是外部的信号,执行的槽函数也全部是和主线程一致的。

💁🏻‍♂️Qt::ConnectionType::AutoConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::AutoConnection);
bash 复制代码
Main Thread QThread(0x12f72c8)
"Construction-Line[8]" QThread(0x12f72c8)
Object-Thread QThread(0x12f72c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0x12f72c8)
">>>Line[18]" QThread(0x12f72c8)

效果

执行到 exec(), 槽函数在主线程执行。

AutoConnection, DirectConnection, QueuedConnection 三者的效果一致。

💁🏻‍♂️Qt::ConnectionType::DirectConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::DirectConnection);
bash 复制代码
Main Thread QThread(0x8372c8)
"Construction-Line[8]" QThread(0x8372c8)
Object-Thread QThread(0x8372c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0x8372c8)
">>>Line[18]" QThread(0x8372c8)

效果

执行到 exec(), 槽函数在主线程执行。

💁🏻‍♂️Qt::ConnectionType::QueuedConnection

cpp 复制代码
connect(this, &MyThread::signal_test, QThread::currentThread(), task, Qt::ConnectionType::QueuedConnection);
bash 复制代码
Main Thread QThread(0x9c72c8)
"Construction-Line[8]" QThread(0x9c72c8)
Object-Thread QThread(0x9c72c8)
"Run-Line[15]" MyThread(0x76fe70)
"Run-Line[27]" MyThread(0x76fe70)
">>>Line[18]" QThread(0x9c72c8)
">>>Line[18]" QThread(0x9c72c8)

效果

执行到 exec(), 槽函数在主线程执行。

💁🏻END

🌟关注我

关注我,学习更多C/C++,算法,计算机知识

B站:

👨‍💻主页:天赐细莲 bilibili

相关推荐
Learn-Share_HY3 分钟前
[Linux]如何配置mailutils郵件服務?
linux·运维·mysql·ubuntu·apache·bash·devops
流星蝴蝶没有剑3 分钟前
Python 电脑桌面——工作量监控大屏
开发语言·python
UrSpecial4 分钟前
页表:从虚拟内存到物理内存的转换
linux·运维·服务器
阿蒙Amon6 分钟前
05. C#入门系列【类、结构、枚举】:从青铜到王者的进阶之路
开发语言·c#
比特森林探险记15 分钟前
《Java vs Go:现代编程语言的核心差异与设计哲学对比》
java·开发语言·golang
草莓啵啵~17 分钟前
Linux--环境的搭建(云服务器)
linux·服务器
悟能不能悟19 分钟前
使用中文作为map的可以,需要注意什么
开发语言·python
海盐泡泡龟24 分钟前
大文件上传如何做断点续传?(分别使用vue、React、java)
java·vue.js·react.js
鲨鱼吃橘子25 分钟前
HTTPS协议原理
网络·c++·网络协议·算法·http·https
liulilittle31 分钟前
CentOS7更新 GLIBC 2.25
linux·运维·服务器·centos