《QT从基础到进阶·三》信号槽关联类型Connection,DirectConnection,QueuedConnection,BlockingQueuedConnection

本文详细解释了Qt中不同类型的信号槽连接(如自动连接、直连、排队连接等)的工作原理,以及在多线程环境下可能遇到的死锁问题,通过示例分析了易错案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、常用的信号槽关联类型
(1)Qt:: Connection
自动连接:默认的方式。信号发出的线程和糟的对象在一个线程的时候相当于:DirectConnection, 如果是在不同线程,则相当于QueuedConnection。
当信号发出后,对应的槽函数将马上被调用。emit语句后的代码将在全部槽函数运行完成后被运行。在这个线程内是顺序运行、同步的。可是与其他线程之间肯定是异步的了。

(2)Qt::DirectConnection(直连方式)(信号与槽函数关系类似于函数调用。同步运行)
直接连接:相当于直接调用槽函数,但是当信号发出的线程和槽的对象不再一个线程的时候,则槽函数是在发出的信号中执行的。
当信号发出后,对应的槽函数将马上被调用。emit语句后的代码将在全部槽函数运行完成后被运行。在这个线程内是顺序运行、同步的。
如果信号所在线程和槽函数所在线程不是一个线程,会强制把槽函数拉到和信号所在的一样的线程来执行,并且是同步执行,这时打印的槽函数线程和信号线程ID是一个ID,emit后面的内容需要等到槽函数执行完毕才执行。

(3)Qt::QueuedConnection(排队方式)(此时信号被塞到信号队列里了,信号与槽函数关系类似于消息通信。异步运行)
当信号发出后。排队到信号队列中,需等到接收对象所属线程的事件循环取得控制权时才取得该信号。调用对应的槽函数。emit语句后的代码将在发出信号后马上被运行。无需等待槽函数运行完成。

(4)Qt::BlockingQueuedConnection (信号和槽必须在不同的线程中。否则就产生死锁)
阻塞连接:发出信号后,当前线程emit后的程序会阻塞,等待槽函数执行完毕后才继续执行,相当于是不同的线程能够同步起来运行。
这个特别要注意,如果线程没启动,那信号和槽依然在同一线程,会死锁,吃了好几次亏了,一定一定要启动线程!!!

(5)Qt::UniqueConnection
防止重复连接。如果当前信号和槽已经连接过了,就不再连接了。同步异步机制与默认方式一样。

容易错的案例1:
MLAlignmentWrapper类中有两个函数,SetModel和startAlignmentMV。
SetModel函数中有信号槽的连接,startAlignmentMV函数中有信号的触发,当触发该信号后会执行
MLAlignmentModel类中的startSpotFind函数功能。
在这里插入图片描述
在这里插入图片描述
startSpotFind函数中有部分代码如下:

connect(this, SIGNAL(updateSpotData(QList<SpotData>))), widget, SLOT(updateCameraSpotData(QList<SpotData>))), Qt::BlockingQueuedConnection)
connect(this, SIGNAL(updateSpotTableData(QList<SpotData>))), widget, SLOT(updateDotsSpotData(QList<SpotData>))), Qt::BlockingQueuedConnection)

emit updateSpotData(m_SpotDataList);
emit updateSpotTableData(m_SpotTableDataList);

MLAlignmentWrapper类已经被MoveToThread,假设这个线程已被启动并执行线程的startALignmentMV方法。emit信号驱动执行m_AlignmentModel的startSpotFind方法,如果MLAlignmentModel没有move到一个线程,那它就属于主线程,this和Widget同属于一个线程,那connect后有Block连接会死锁,如果move到了一个线程,那emit startMVSignal后m_AlignmentModel就开启了一个线程,内部有Block连接可以走通。如果把startALignmentMV函数中的emit注释,把上面的注释打开,现在startALignmentMV是一个线程的方法在执行m_AlignmentModel->startSpotFind,不管MLAlignmentModel有没有move到一个线程,startSpotFind都属于MLAlignmentWrapper线程,因为我直接调用的是AlignmentModel方法没有启动线程,在startSpotFind内部打印线程ID是和MLAlignmentWrapper线程ID一样的,那startSpotFind中的this属于MLAlignmentWrapper线程,Widget属于主线程,不同线程自然Block连接方式可以走通。

现在做个修改:SetModel里的connect加个Block连接方式,不把MLAlignmentWrapper类move到一个线程,那么MLAlignmentWrapper就属于主线程,MLAlignmentModel类move到一个线程,在emit startMVSignal后就启动了MLAlignmentModel线程,并执行connect内容,因为this和m_AlignmentWidget属于不同的线程,想要同步执行,后面得要加Block连接,这时候主线程需要等待startSpotFind执行完才继续走,而内部connect连接的this属于MLAlignmentModel线程,alignmentWidget属于主线程,想要同步执行后面得要加Block连接,这样的话就有个问题,前面是主线程需要等MLAlignmentModel线程执行完才走,后面是MLAlignmentModel线程需要等主线程执行完才走,形成死循环。

易错的案例2:
在这里插入图片描述
在这里插入图片描述
回调函数在线程中循环被调用,connect在主线程被连接,这样的话emit属于子线程触发,在主线程执行showImage(),后面加block属于不同线程同步执行,emit之后需要等主线程showImage走完才接着走子线程的qDebug打印。如果我在主线程执行了子线程的join()功能,那么会阻塞主线程直到子线程结束,这样的话,子线程需要等主线程走完才走,而主线程被阻塞需要等子线程走完,形成死循环。

在这里插入图片描述

📢博客主页: 主页
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 梦回阑珊 原创,首发于 优快云,转载注明出处🙉
📢代码改变世界,你来改变代码!✨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦回阑珊

一毛不嫌多,一分也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值