QT信号与槽的第五个参数 大话讲解
笔者在刚接触QT信号与槽的时候很长的一段时间基本上都是用QT信号与槽最基础的形式去连接。发现大多数情况下也没啥问题。后来某次参加一次面试的时候被问到了这个问题,才有兴趣去了解了一下,用AI和查阅一下资料发现也是很官方很学术的去讲解,但是笔者是一个喜欢考虑所有场景、刨根问底把抽象的学术讲解形象化大白话理解的的一个人。比如讲到DirectConnection和QueuedConnection大部分只是官方的去讲一下,一个是通常相同线程对象使用,一个是在通常不同线程对象连接的时候使用(然后学术性的一堆啥啥),笔者就想如果调换一下场景呢,怎么使用,最终产生什么影响?原理是啥呢。
Qt::AutoConnection |
自动判断(默认) |
Qt::DirectConnection |
直接调用 |
Qt::QueuedConnection |
排队调用 |
Qt::BlockingQueuedConnection |
阻塞排队 |
Qt::UniqueConnection |
防止重复连接 |
QT信号与槽中一共如上的几个参数,但是最常用的其实是前三个。
一、Qt::AutoConnection
sender 和 receiver 在同一线程 自动推导成------->DirectConnection;
不在同一线程:------->QueuedConnection;
两者有什么区别呢?往下看:
二、Qt::DirectConnection
直接调用槽函数。
啥意思呢?
常用情景:发送者和接收者同一个线程:
qDebug() << 1;
emit sig_test();
qDebug() << 2;
执行顺序:1----->槽函数----->2
QT会把槽函数立刻执行,而且槽函数也是在信号发送的线程执行的,再去执行信号下面的代码块。一般同一线程的都用这种情况。
情况二:发送者和接收者不在同一个线程呢。
//子类的定义
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork()
{
qDebug() << "currentThread:" << QThread::currentThread();
qDebug() << "object thread:" << this->thread();
}
};
//主线程代码
QThread* t = new QThread;
Worker* w = new Worker;
w->moveToThread(t);
t->start();
//强制 DirectConnection
connect(this, &MainWindow::sig_test,
w, &Worker::doWork,
Qt::DirectConnection);
//主线程发信号
void main(){
qDebug() << "emit thread:" << QThread::currentThread();
emit sig_test();
qDebug()<<"信号后面的内容";
}
结果是:emit thread: 0xA (主线程)
currentThread: 0xA (主线程) ← 关键点
object thread: 0xB (子线程)
信号后面的内容
这里槽函数在信号发生者的线程执行,主线程的事件队列一看,卧槽来新活了 插个队吧 把你这个dowrok往前排排 我当前的后面的代码有没有执行完我先不管了,立刻执行,再去管eimt后面的代码块!这是一种很危险的操作,子类所属线程在子线程,但是槽函数执行在了主线程。
三、Qt::QueuedConnection
常用情况:两个对象在不同线程进行通讯:
//主程序代码
qDebug() << "A";
emit sig_test();
qDebug() << "B";
//子线程的类的触发的槽函数
slot:
qDebug() << "S";
打印结果:A----->B------>S
实质上是 槽函数在接收者的线程执行,并且放在接收者线程的任务队列的末端,处理完了手头事情,再去调槽函数,而主线程发送完信号之后,马上执行下面的代码,不用管槽函数执行,大白话就是,主线程我自己干我自己的下面的事情,你槽函数爱在哪执行就在哪执行吧,执行管理槽函数的线程怎么去执行和我也没啥关系了
情况二:
两个对象在同线程进行通讯:
一、完整示例代码
#include <QCoreApplication>
#include <QDebug>
#include <QObject>
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork()
{
qDebug() << "S (slot) thread:" << QThread::currentThread();
}
};
class MainWindow : public QObject
{
Q_OBJECT
signals:
void sig_test();
public:
void run()
{
qDebug() << "A (before emit) thread:" << QThread::currentThread();
emit sig_test();
qDebug() << "B (after emit) thread:" << QThread::currentThread();
}
};
//main 函数(关键:同线程 + Queued)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
MainWindow m;
Worker w;
QObject::connect(&m, &MainWindow::sig_test,
&w, &Worker::doWork,
Qt::QueuedConnection);
m.run();
return a.exec();
}
最终打印顺序(重点)
A (before emit) thread: 0x主线程
B (after emit) thread: 0x主线程
S (slot) thread: 0x主线程
信号发出后,槽函数也在主线程执行,主线程的事件队列一看,妈耶,我的当前的run() 还没执行完呢,你这槽函数往后排排吧,于是先把run函数后面的代码执行,最后执行槽函数。
所以信号与槽的第五个参数主要关注这两个区别即可!
四、Qt::UniqueConnection(防重复连接)
防止重复 connect 同一信号槽
如果你写:
connect(a, &A::sig, b, &B::slot);
connect(a, &A::sig, b, &B::slot);
//slot 会执行两次
//加 UniqueConnection
connect(a, &A::sig,
b, &B::slot,
Qt::UniqueConnection);
重复 connect 会被忽略:slot 只执行一次Qt 内部会检查:
(sender, signal, receiver, slot)
是否已存在连接
五、Qt::BlockingQueuedConnection(阻塞队列连接)
跨线程调用 + 等待执行完成(同步阻塞)
emit → 投递到目标线程 → 等 slot 执行完 → 才返回;
connect(sender, &Sender::sig,
worker, &Worker::doWork,
Qt::BlockingQueuedConnection);
线程效果
假设:
- A线程 emit
- B线程 slot
那么:
A线程:
emit(sig)
↓(阻塞住)
等待B线程执行slot
↓
slot执行完
↓
A线程继续
特点
✔ 跨线程同步调用
✔ 自动线程切换
✔ emit 会卡住等待
非常重要的风险(重点)
❌ 禁止同线程使用
如果:
同线程 + BlockingQueuedConnection
会发生:
死锁
因为:
- A线程发消息
- A线程又等自己执行完
- 但自己被阻塞了
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)