(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

相关推荐
星辰大海的精灵几秒前
如何确保全球数据管道中的跨时区数据完整性和一致性
java·后端·架构
学习编程的gas几秒前
Linux开发工具——gcc/g++
linux·运维·服务器
大大。3 分钟前
van-tabbar-item选中active数据变了,图标没变
java·服务器·前端
嵌入式成长家6 分钟前
ubuntu rules 使用规则
linux·ubuntu·rules 使用规则
nc_kai6 分钟前
Flutter 之 每日翻译 PreferredSizeWidget
java·前端·flutter
_可乐无糖12 分钟前
AWS WebRTC: 判断viewer端拉流是否稳定的算法
linux·服务器·webrtc·aws
shootero@126.com13 分钟前
R语言开发记录,一
开发语言·r语言
Codebee16 分钟前
OneCode:AI时代的先锋——注解驱动技术引领开发范式变革
java
勤奋的知更鸟16 分钟前
Java 编程之状态模式
java·开发语言·状态模式
清醒的兰24 分钟前
Qt 基于TCP套接字编程
网络·qt·tcp