Qt信号槽的一些事Qt::带返回值的信号发射方式

更新时间:2023-06-14 02:55:30 阅读: 评论:0

Qt信号槽的⼀些事Qt::带返回值的信号发射⽅式
⼀般来说,我们发出信号使⽤emit这个关键字来操作,但是会发现,emit并不算⼀个调⽤,所以它没有返回值。那么如果我们发出这个信号想获取⼀个返回值怎么办呢?
两个办法:1.通过出参形式返回,引⽤或者指针的⽅式带回;⽐如emit sig(int& i)或者emit sig(void* pointer),但是这个⽅法有⼀个弊端,稍后介绍第⼆种⽅式会提醒。
2.通过qt⾃带的invoke机制调⽤:参考⽂档对QMetaObject::invokeMethod的说明:Invokes the member (a signal or a slot name) on the object obj.也就是说回调是可以回调信号或者槽的。⼀般来说,我们使⽤invokeMethod是在⼦线程需要调度UI操作的时候(已经有很多⽂章详细说明了使⽤⽅式,不再赘述),因为UI操作只能在主线程中使⽤(否则会出现未定义错误),通过这种回调⽅式,让要操作的事件回到主线程时间⽚的时候再来执⾏。⼤部分情况下,我们把UI操作封装在⼀个槽⾥,⽤回调⽅式来调度。同样信号也可以⽤这种⽅式,但是有⼏点需要注意的是,1.调⽤回调的连接⽅式:如果信号和连接槽在⼀个线程内,那么必须⽤Qt::DirectConnection或者
Qt::AutoConnection,这样的话,保证信号回调后,线程会等待信号连接槽执⾏完毕,才可能取到我们需要的返回值;如果使⽤了
Qt::QueuedConnection,那么信号只是负责把事件交给事件队列,然后马上做出返回,这样,是否有返回值就⽆法确定了(这也就是第⼀个⽅法的弊端,因为信号发射是根据信号和槽各⾃的线程情况来选择的连接⽅式).如果信号和槽在两个线程中,那么⾸先肯定不能使⽤Qt::DirectConnection,除⾮你很清楚连接槽的动作是否保证了线程安全。但根据第⼀条的说明,也不能使⽤Qt::QueuedConnection。不过还好qt提供了⼀个额外的连接⽅式就是Qt::BlockingQueuedConnection,这个连接⽅式会阻塞住发射信号的线程⼀直等到队列连接槽返回后,才会恢复阻塞,这样就可以保证我们能拿到真正的返回值。(但是使⽤这种⽅式需要你清楚的知道,发射线程是否允许阻塞和连接槽是否对这个阻塞线程有什么特别的操作,⼀般来说,如果这个线程并不是由你⾃⼰控制的话,不要随便尝试去阻塞别⼈的线程,因为你并不清楚别⼈线程的执⾏逻辑)
调⽤⽅式⼤致代码如下bool bReturn; QMetaObject::invokeMethod(&object, "sig",
Qt::DirectConnection/*Qt::QueuedConnection*/, Q_RETURN_ARG(bool, bReturn), Q_ARG(int, i));
注:此⽂是站在Qt5的⾓度说的,对于Qt4部分是不适⽤的。
1.先说Qt信号槽的⼏种连接⽅式和执⾏⽅式。
1)Qt信号槽给出了五种连接⽅式:
Qt::AutoConnection0⾃动连接:默认的⽅式。信号发出的线程和糟的对象在⼀个线程的时候相当于:DirectConnection, 如果是在不同线程,则相当于QueuedConnection
Qt::DirectConnection1直接连接:相当于直接调⽤槽函数,但是当信号发出的线程和槽的对象不再⼀个线程的时候,则槽函数是在发出的信号中执⾏的。
Qt::QueuedConnection2队列连接:内部通过postEvent实现的。不是实时调⽤的,槽函数永远在槽函数对象所在的线程中执⾏。如果信号参数是引⽤类型,则会另外复制⼀份的。线程安全的。
Qt::BlockingQueuedConnection3阻塞连接:此连接⽅式只能⽤于信号发出的线程(⼀般是先好对象的线程) 和 槽函数的对象不再⼀个线程中才能⽤。通过信号量+postEvent实现的。不是实时调⽤的,槽函数永远在槽函数对象所在的线程中执⾏。但是发出信号后,当前线程会阻塞,等待槽函数执⾏完毕后才继续执⾏。
医药企业管理
Qt::UniqueConnection0x80防⽌重复连接。如果当前信号和槽已经连接过了,就不再连接了。
2)信号槽的调⽤⽅式和线程:
UniqueConnection 模式:严格说不算连接⽅式,⽅式就是4中,此只是⼀个附加的参数。不讨论。
AutoConnection 模式:这个模式是默认的,但其可以看作是DirectConnection和QueuedConnection的⾃动选择,直接分析那两种也就⾏了。
发出信号,调⽤槽的⽅式也可以简单的分为两种:同步调⽤和异步调⽤酒店宣传文案
同步调⽤:发出信号后,当前线程等待槽函数执⾏完毕后才继续执⾏。
异步调⽤:发出信号后,⽴即执⾏剩下逻辑,不关⼼槽函数什么时候执⾏。
所以有下表:
线程/模
DirectConnection QueuedConnection BlockingQueuedConnection
相同线
直接调⽤,同步调⽤。通过事件进⾏队列调⽤。异步调⽤.不可⽤
不同线程直接调⽤。同步调⽤。槽函数在发出信号
堂联
的线程执⾏。有线程安全隐患。
通过事件进⾏队列调⽤。异步调⽤.槽函数在
对象所在的线程执⾏。线程安全。
通过事件进⾏阻塞调⽤。同步调⽤。槽函数在对象所在
的线程执⾏。线程安全。
室内设计英文
Qt事件
循环依赖直接调⽤,不依赖Qt事件循环
通过事件进⾏队列调⽤。依赖,槽函数所在
对象的线程必须启⽤Qt事件循环
通过事件进⾏队列调⽤,⽤信号量实现阻塞。依赖,槽
函数所在对象的线程必须启⽤Qt事件循环
2.Qt信号连接多个槽,调⽤顺序。
先说基本原则:
槽函数开始调⽤的顺序和连接的顺序是⼀致的。
但是,上⾯也说了,有同步调⽤和异步调⽤。
对于同步调⽤,你观察的结果和基本原则⼀样。
但是对于异步调⽤,可能你最先连接的它,但是可能其他都执⾏完毕了,但是其还没执⾏。是因为对于异步调⽤:是开始调⽤的时候,⽣成⼀个需要调⽤这个函数的事件,然后放到事件队列⾥。然后⽴即返回,去执⾏调⽤其他槽函数或者槽函数都执⾏了,不关⼼槽函数的执⾏状态的。等到事件队列⾥任务轮到此事件再去调⽤。
3.信号的返回值。
⼤都说Qt信号槽不能使⽤返回值。其实不不准确的,Qt5中,信号槽是有返回值的。只是Qt的⼀个信
号可以连接多个槽,还有同步调⽤和异步调⽤的问题,没发⽀持的很好,所以,返回值虽有,但只是鸡肋。
先说下返回值的规则把:
同步调⽤才有返回值,异步调⽤的返回值永远为返回值类型默认构造函数出来的。
连接的多个槽都返回值,那么结果是最后调⽤(连接)的那个。
也就是说对于QueuedConnection连接的信号槽,永远只是返回返回类型的默认构造函数的。对于AutoConnection连接的,如果发出信号的线程和槽函数线程不同亦然。
4.信号参数的安全问题:
因为⼀个信号可以连接多个槽函数,如果参数是T * 或者是T &话会不会第⼀个槽函数改变参数的值,然后第⼆此调⽤的参数就已经不是信号发出的值?
1)对于T &: 在同步调⽤中则是变化的,不可⽤于异步,不可跨线程。所以BlockingQueuedConnection⽅式的同步也不⾏。(T& 不可⽤在队列调⽤(QueuedConnection)和阻塞调⽤(BlockingQueuedConnection)中。只能使⽤const T &。)
因为同步调⽤,你可以理解成直接调⽤,那么连接多个槽函数就相当于直接连续调⽤多个函数。类似于:
// 函数原型都是:void  (int &a )
int a;
fun1(a);
fun2(a)
·····
// 函数原型都是:void  (int * a )
int a;
pfun1(&a);
pfun2(&a)
·····
这样,当第⼀个函数执⾏改变参数值之后,其后的函数调⽤都要受影响。
2) 对于T *,最好不要同时连接多个槽。
对于同步调⽤:是⼀个接着⼀个调⽤的,执⾏顺序类似上⾯,所以值也是每次调⽤也会变化的。
岩井俊二
对于异步调⽤:其内容确实不确定的,因为异步调⽤的时间是不可控的。如果还有跨线程相关,则还有线程安全问题。
5.信号槽性能损失:
注:仅仅代码层进⾏的理论分析,⾮实际测试,不严谨,不权威。
关于信号槽(很多吐槽Qt就是说的这个):
(1)Qt4语法的,都说是匹配字符串,其实只是链接信号槽的⽤的匹配字符串 的⽅法,通过字符串找到信号和槽在QMeatObject⾥存的索引位置int类型,还有槽函数的索引,然后调⽤的时候通过索引号⽤switch去区分的 发射的那个函数,然后取出对应的链接槽的list,循环检测槽函数的参数是否匹配,然后调⽤槽函数。。这个链接时会耗时查找,但是你能有多少信号?这个链 接也耗时不多,调⽤的时候耗时主要就是在参数匹配上了。
甘国宝
(2)Qt5 语法的,Qt5 的槽函数链接和执⾏是基于模板实现的,函数对象。信号和槽的参数问题是编译时检查的,执⾏效率更⾼,但是编译就慢点了。链接时也是通过信号的地址找到其的 信号索引,⾄于槽函数直接是⽣成⼀个函数对象的,然后调⽤的时候也是先switch找到发射的信号,取出list,然后逐个调⽤其储存的函数对象,所以对 于Qt5 语法的信号槽,调⽤性能损失⼏乎可以说⽆的。
(3)链接的信号槽的时候,Qt::UniqueConnection的链接⽅式会对已经链接过的此先好的槽函数进⾏遍历,会有链接时的损失。其他链接的损失就在上⾯说过了。
继续用英语怎么说(3)在信号槽调⽤的时候,还有⼀些链接⽅式和线程的判断和为了安全问题的锁操作。关于这个就还涉及到调⽤槽函数的线程问题。
对于同线程直接调⽤,较函数对象直接调⽤的损失,就只有链接⽅式和线程的判断的⼏个if 分⽀和 锁的操作。
对于线程间通讯的调⽤,跨线程。信号槽内部也是通过Qt事件循环机制实现的,跨线程就不是时时调⽤了,主要是安全了,对于性能有没有损失没法评论的。对于跨线程阻塞的调⽤,这个也是事件实现,只是但发射信号的线程会阻塞,这个找不到对应的直接调⽤的⽐较,也不好说。
关于信号槽Qt是作何很多⽅便使⽤和安全调⽤,较之函数指针,性能会有损失,但是也没损失多少的。对于函数对象调⽤,Qt5语法的调⽤,⼏乎是不损失什么的。
面包机哪个牌子好注:此⽂是个⼈根据⽂档,源码和⾃⼰写⼩例⼦测试得出的总结,如有错误请您指出。

本文发布于:2023-06-14 02:55:30,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/fan/89/1037454.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:线程   信号   函数   连接   事件   返回值   阻塞
相关文章
留言与评论(共有 0 条评论)
   
验证码:
推荐文章
排行榜
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图