前言
Qt的信号槽的连接方式有五种,每种代表的含义,从文档可以知道
Qt::AutoConnection:(默认)如果接收者是发出信号的线程,则使用Qt::DirectConnection。否则,将使用Qt::QueuedConnection。连接类型在信号发出时被确定。
Qt::DirectConnection:当信号被发出时,槽被立即调用。该槽是在信号线程中执行的。
Qt::QueuedConnection:当控制权返回到接收方线程的事件循环时,槽被调用。该槽在接收方的线程中执行。
Qt::BlockingQueuedConnection:与Qt::QueuedConnection相同,只是信号线程会阻塞,直到槽返回。如果接收者在信号线程中,就不能使用这种连接,否则应用程序会陷入死锁。
Qt::UniqueConnection:这是一个可以与上述任何一种连接类型相结合的标志,使用的方法是和之前的标志或运算。当Qt::UniqueConnection被设置时,如果连接已经存在,QObject::connect()将失败(即如果相同的信号已经连接到同一对对象的同一槽中)。
测试
现在编写代码测试一下
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QDateTime>
#include <thread>
#include <chrono>
#include <iostream>
#include <QTimer>
void myMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
QString log("[%1] [%2] %3");
QString dateTime = QDateTime::currentDateTime().toString("yyyy/MM/dd hh:mm:ss.zzz");
QChar ch;
switch (type) {
case QtDebugMsg:
= 'D';
ch break;
case QtInfoMsg:
= 'I';
ch break;
case QtWarningMsg:
= 'W';
ch break;
case QtCriticalMsg:
= 'C';
ch break;
case QtFatalMsg:
= 'F';
ch break;
}
= log.arg(dateTime, ch, msg);
log std::cout << log.toStdString() << std::endl;
}
struct Tool
{
QString toolName;
};
struct TaskData
{
int taskId;
QString worker;
*tool = nullptr;
Tool };
QMap<QThread *, QString> threadMap;
QStringList names{
"dhimmied",
"chumhaeds",
"hulmol",
"birran",
"vamets",
"ogmath",
};
class ThreadA : public QThread
{
Q_OBJECT
public:
explicit ThreadA(QObject *parent = nullptr) : QThread(parent){}
protected:
void run() override
{
[QThread::currentThread()] = metaObject()->className();
threadMapfor (int i = 0; i < names.size(); ++i) {
qDebug() << "begin dispatchTask" << "thread: " << threadMap[QThread::currentThread()];
{"t" + QString::number(i)};
Tool tool{i, names[i], &tool};
TaskData tdemit dispatchTask(td);
qDebug() << "finish dispatchTask" << "thread: " << threadMap[QThread::currentThread()];
(1000);
msleep}
}
signals:
void dispatchTask(const TaskData &data);
};
class ThreadB : public QObject
{
Q_OBJECT
public:
explicit ThreadB(QObject *parent = nullptr) : QObject(parent)
{
auto thread = new QThread;
(thread);
moveToThread->start();
thread}
void doTask(const TaskData &data)
{
qDebug() << "doTask: {" << data.taskId << ", " << data.worker
// << "tool: " << data.tool->toolName
<< "}" << "thread: " << threadMap[QThread::currentThread()];
std::this_thread::sleep_for(std::chrono::seconds(3));
}
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
qInstallMessageHandler(myMessageOutput);
;
ThreadA a;
ThreadB bqRegisterMetaType<TaskData>("TaskData");
QObject::connect(&a, &ThreadA::dispatchTask, &b, &ThreadB::doTask,
Qt::ConnectionType::AutoConnection);
.start();
a[b.thread()] = b.metaObject()->className();
threadMap[QThread::currentThread()] = "mainThread";
threadMapreturn QCoreApplication::exec();
}
#include "test_signal_slot.moc"
说明
发送任务线程A间隔1s发送信号给接收任务线程B,线程B每个任务执行需要3s,存在滞后性。线程B采用moveToThread方式创建可以理解为在线程B中运行了一个额外的事件时间循环可以实时响应信号执行槽函数,也可以这么写:
class ThreadB : public QThread
{
Q_OBJECT
public:
explicit ThreadB(QObject *parent = nullptr) : QThread(parent) {}
protected:
void run() override
{
[QThread::currentThread()] = ThreadB::metaObject()->className();
threadMapQEventLoop ev;
while (true) {
.processEvents();
ev(100);
msleep}
}
public:
void doTask(const TaskData &data)
{
qDebug() << "doTask: {" << data.taskId << ", " << data.worker
// << "tool: " << data.tool->toolName
<< "}" << "thread: " << threadMap[QThread::currentThread()];
std::this_thread::sleep_for(std::chrono::seconds(3));
}
};
结果分析
Qt::AutoConnection
在Qt::AutoConnection
下,任务发送和执行按照各自的时序运行,1s发送一次,3s执行一次,输出结果:
[2021/12/31 15:48:39.281] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:39.281] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:39.281] [D] doTask: { 0 , "dhimmied" } thread: "ThreadB"
[2021/12/31 15:48:40.281] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:40.281] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:41.281] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:41.282] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:42.281] [D] doTask: { 1 , "chumhaeds" } thread: "ThreadB"
[2021/12/31 15:48:42.282] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:42.282] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:43.282] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:43.282] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:44.282] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:48:44.282] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:48:45.282] [D] doTask: { 2 , "hulmol" } thread: "ThreadB"
[2021/12/31 15:48:48.282] [D] doTask: { 3 , "birran" } thread: "ThreadB"
[2021/12/31 15:48:51.282] [D] doTask: { 4 , "vamets" } thread: "ThreadB"
[2021/12/31 15:48:54.282] [D] doTask: { 5 , "ogmath" } thread: "ThreadB"
Qt::DirectConnection
而在Qt::DirectConnection
下,所有的信号关联的槽函数都在发送信号的线程执行
[2021/12/31 15:49:20.064] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:20.064] [D] doTask: { 0 , "dhimmied" } thread: "ThreadA"
[2021/12/31 15:49:23.065] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:49:24.065] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:24.065] [D] doTask: { 1 , "chumhaeds" } thread: "ThreadA"
[2021/12/31 15:49:27.065] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:49:28.065] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:28.065] [D] doTask: { 2 , "hulmol" } thread: "ThreadA"
[2021/12/31 15:49:31.066] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:49:32.066] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:32.066] [D] doTask: { 3 , "birran" } thread: "ThreadA"
[2021/12/31 15:49:35.066] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:49:36.067] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:36.067] [D] doTask: { 4 , "vamets" } thread: "ThreadA"
[2021/12/31 15:49:39.067] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:49:40.067] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:49:40.067] [D] doTask: { 5 , "ogmath" } thread: "ThreadA"
[2021/12/31 15:49:43.067] [D] finish dispatchTask thread: "ThreadA"
Qt::QueuedConnection
Qt::AutoConnection
在多线程连接时和
Qt::QueuedConnection
一样
Qt::BlockingQueuedConnection
Qt::BlockingQueuedConnection
会在另外一个线程槽函数执行完之前阻塞,等到执行完才往下执行
[2021/12/31 15:53:30.054] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:30.054] [D] doTask: { 0 , "dhimmied" } thread: "ThreadB"
[2021/12/31 15:53:33.054] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:53:34.055] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:34.055] [D] doTask: { 1 , "chumhaeds" } thread: "ThreadB"
[2021/12/31 15:53:37.055] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:53:38.055] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:38.056] [D] doTask: { 2 , "hulmol" } thread: "ThreadB"
[2021/12/31 15:53:41.056] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:53:42.056] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:42.056] [D] doTask: { 3 , "birran" } thread: "ThreadB"
[2021/12/31 15:53:45.057] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:53:46.057] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:46.057] [D] doTask: { 4 , "vamets" } thread: "ThreadB"
[2021/12/31 15:53:49.057] [D] finish dispatchTask thread: "ThreadA"
[2021/12/31 15:53:50.057] [D] begin dispatchTask thread: "ThreadA"
[2021/12/31 15:53:50.058] [D] doTask: { 5 , "ogmath" } thread: "ThreadB"
[2021/12/31 15:53:53.058] [D] finish dispatchTask thread: "ThreadA"
另外,通过队列的方式,信号槽中有数据的话会缓存到事件队列,但是如果指针指向发出信号函数的局部变量,可能该变量被析构导致调用出错,此时需要使用共享指针更安全;事件队列如果步调一直不一致也会称为程序的安全隐患,比如队列会溢出
其他
在线程中需要通过主线程执行函数,比如控件的update(),可以使用QTimer::singleShot
QTimer::singleShot(0, [widget] { widget->update(); })