(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

相关推荐
靡樊几秒前
Linux:基础开发工具
linux
孤独且没人爱的纸鹤几秒前
C++ 二叉搜索树(Binary Search Tree, BST)深度解析与全面指南:从基础概念到高级应用、算法优化及实战案例
c语言·数据结构·c++·算法
Heris994 分钟前
零基础3分钟快速掌握 ——Linux【终端操作】及【常用指令】Ubuntu
linux·c语言·开发语言·ubuntu
亚林瓜子4 分钟前
BC-Linux8.6上面离线手动安装Docker引擎
linux·运维·docker·容器·bc-linux
我是唐青枫6 分钟前
Linux firewalld 命令详解
linux·运维·网络
理想不理想v10 分钟前
前端开发工程师需要学什么?
java·前端·vue.js·webpack·node.js
赶路人儿11 分钟前
IntelliJ IDEA配置(mac版本)
java·macos·intellij-idea
jjw_zyfx11 分钟前
docker 的各种操作
java·docker·eureka
生财13 分钟前
获取字 short WORD 上指定的位是否有效
java·服务器·c#
贺椿椿14 分钟前
ensp静态路由实验
linux·网络·智能路由器