文章目录
💁🏻前言
在 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
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