Qt信号槽的一些事

注:此文是站在Qt5的角度说的,对于Qt4部分是不适用的。

1.先说Qt信号槽的几种连接方式和执行方式。

1)Qt信号槽给出了五种连接方式:

Qt::AutoConnection 0 自动连接:默认的方式。信号发出的线程和糟的对象在一个线程的时候相当于:DirectConnection, 如果是在不同线程,则相当于QueuedConnection
Qt::DirectConnection 1 直接连接:相当于直接调用槽函数,但是当信号发出的线程和槽的对象不再一个线程的时候,则槽函数是在发出的信号中执行的。
Qt::QueuedConnection 2 队列连接:内部通过postEvent实现的。不是实时调用的,槽函数永远在槽函数对象所在的线程中执行。如果信号参数是引用类型,则会另外复制一份的。线程安全的。
Qt::BlockingQueuedConnection 3 阻塞连接:此连接方式只能用于信号发出的线程(一般是先好对象的线程) 和 槽函数的对象不再一个线程中才能用。通过信号量+postEvent实现的。不是实时调用的,槽函数永远在槽函数对象所在的线程中执行。但是发出信号后, 当前线程会阻塞,等待槽函数执行完毕后才继续执行。
Qt::UniqueConnection 0x80 防止重复连接。如果当前信号和槽已经连接过了,就不再连接了。

2)信号槽的调用方式和线程:

UniqueConnection 模式:严格说不算连接方式,方式就是4中,此只是一个附加的参数。不讨论。

AutoConnection 模式:这个模式是默认的,但其可以看作是DirectConnection和QueuedConnection的自动选择,直接分析那两种也就行了。

发出信号,调用槽的方式也可以简单的分为两种:同步调用和异步调用

同步调用:发出信号后,当前线程等待槽函数执行完毕后才继续执行。

异步调用:发出信号后,立即执行剩下逻辑,不关心槽函数什么时候执行。

所以有下表:

线程/模式 DirectConnection QueuedConnection BlockingQueuedConnection
相同线程 直接调用,同步调用。 通过事件进行队列调用。异步调用. 不可用
不同线程 直接调用。同步调用。

槽函数在发出信号的线程执行。

有线程安全隐患。

通过事件进行队列调用。异步调用.

槽函数在对象所在的线程执行。

线程安全。

通过事件进行阻塞调用。同步调用。

槽函数在对象所在的线程执行。

线程安全。

2.Qt信号连接多个槽,调用顺序。

先说基本原则:

槽函数开始调用的顺序和连接的顺序是一致的。

但是,上面也说了,有同步调用和异步调用。

对于同步调用,你观察的结果和基本原则一样。

但是对于异步调用,可能你最先连接的它,但是可能其他都执行完毕了,但是其还没执行。是因为对于异步调用:是开始调用的时候,生成一个需要调用这个 函数的事件,然后放到事件队列里。然后立即返回,去执行调用其他槽函数或者槽函数都执行了,不关心槽函数的执行状态的。等到事件队列里任务轮到此事件再去 调用。

3.信号的返回值。

大都说Qt信号槽不能使用返回值。其实不不准确的,Qt5中,信号槽是有返回值的。只是Qt的一个信号可以连接多个槽,还有同步调用和异步调用的问题,没发支持的很好,所以,返回值虽有,但只是鸡肋。

先说下返回值的规则把:

  • 同步调用才有返回值,异步调用的返回值永远为返回值类型默认构造函数出来的。
  • 连接的多个槽都返回值,那么结果是最后调用(连接)的那个。

也就是说对于QueuedConnection连接的信号槽,永远只是返回返回类型的默认构造函数的。对于AutoConnection连接的,如果发出信号的线程和槽函数线程不同亦然。

测试小例子地址:https://github.com/dushibaiyu/DsbyLiteExample/tree/master/QtSignalsSlotTest

4.信号参数的安全问题:

因为一个信号可以连接多个槽函数,如果参数是T * 或者是T &话会不会第一个槽函数改变参数的值,然后第二此调用的参数就已经不是信号发出的值?

1)对于T &: 在同步调用中则是变化的,不可用于异步,不可跨线程。所以BlockingQueuedConnection方式的同步也不行。(T& 不可用在队列调用(QueuedConnection)和阻塞调用(BlockingQueuedConnection)中。只能使用const T &。)

因为同步调用,你可以理解成直接调用,那么连接多个槽函数就相当于直接连续调用多个函数。类似于:

01 // 函数原型都是:void  (int &a )
02 int a;
03 fun1(a);
04 fun2(a)
05 ·····
06
07 // 函数原型都是:void  (int * a )
08 int a;
09 pfun1(&a);
10 pfun2(&a)
11 ·····

这样,当第一个函数执行改变参数值之后,其后的函数调用都要受影响。

2) 对于T *,最好不要同时连接多个槽。

对于同步调用:是一个接着一个调用的,执行顺序类似上面,所以值也是每次调用也会变化的。

对于异步调用:其内容确实不确定的,因为异步调用的时间是不可控的。如果还有跨线程相关,则还有线程安全问题。

5.信号槽性能损失:

注:仅仅代码层进行的理论分析,非实际测试,不严谨,不权威。

关于信号槽(很多吐槽Qt就是说的这个):

(1)Qt4语法的,都说是匹配字符串,其实只是链接信号槽的用的匹配字符串 的方法,通过字符串找到信号和槽在QMeatObject里存的索引位置int类型,还有槽函数的索引,然后调用的时候通过索引号用switch去区分的 发射的那个函数,然后取出对应的链接槽的list,循环检测槽函数的参数是否匹配,然后调用槽函数。。这个链接时会耗时查找,但是你能有多少信号?这个链 接也耗时不多,调用的时候耗时主要就是在参数匹配上了。

(2)Qt5 语法的,Qt5 的槽函数链接和执行是基于模板实现的,函数对象。信号和槽的参数问题是编译时检查的,执行效率更高,但是编译就慢点了。链接时也是通过信号的地址找到其的 信号索引,至于槽函数直接是生成一个函数对象的,然后调用的时候也是先switch找到发射的信号,取出list,然后逐个调用其储存的函数对象,所以对 于Qt5 语法的信号槽,调用性能损失几乎可以说无的。

(3)链接的信号槽的时候,Qt::UniqueConnection的链接方式会对已经链接过的此先好的槽函数进行遍历,会有链接时的损失。其他链接的损失就在上面说过了。
(3)在信号槽调用的时候,还有一些链接方式和线程的判断和为了安全问题的锁操作。关于这个就还涉及到调用槽函数的线程问题。

对于同线程直接调用,较函数对象直接调用的损失,就只有链接方式和线程的判断的几个if 分支和 锁的操作。
对于线程间通讯的调用,跨线程。信号槽内部也是通过Qt事件循环机制实现的,跨线程就不是时时调用了,主要是安全了,对于性能有没有损失没法评论的。对于跨线程阻塞的调用,这个也是事件实现,只是但发射信号的线程会阻塞,这个找不到对应的直接调用的比较,也不好说。
关于信号槽Qt是作何很多方便使用和安全调用,较之函数指针,性能会有损失,但是也没损失多少的。对于函数对象调用,Qt5语法的调用,几乎是不损失什么的。

注:此文是个人根据文档,源码和自己写小例子测试得出的总结,如有错误请您指出。

时间: 2024-10-08 17:49:26

Qt信号槽的一些事的相关文章

Qt高级——Qt信号槽机制源码解析

Qt高级--Qt信号槽机制源码解析 基于Qt4.8.6版本 一.信号槽机制的原理 1.信号槽简介 信号槽是观察者模式的一种实现,特性如下:A.一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知:B.一个槽就是一个观察者,通常就是在被观察的对象发生改变的时候--也可以说是信号发出的时候--被调用的函数:C.信号与槽的连接,形成一种观察者-被观察者的关系:D.当事件或者状态发生改变的时候,信号就会被发出:同时,信号发出者有义务调用所有注册的对这个事件(信号)感兴趣的函数(槽).信号和

Qt信号槽机制源码解析

Qt信号槽机制源码解析 来源 https://blog.51cto.com/9291927/2070398 一.信号槽机制的原理 1.信号槽简介 信号槽是观察者模式的一种实现,特性如下:A.一个信号就是一个能够被观察的事件,或者至少是事件已经发生的一种通知:B.一个槽就是一个观察者,通常就是在被观察的对象发生改变的时候——也可以说是信号发出的时候——被调用的函数:C.信号与槽的连接,形成一种观察者-被观察者的关系:D.当事件或者状态发生改变的时候,信号就会被发出:同时,信号发出者有义务调用所有注

QT信号槽与Delphi事件的对比

最近学QT,对信号槽机制感到有点新鲜: QObject::connect(slider, SIGNAL(valueChanged(int)), lcd, SLOT(display(int))); 自己总结其原理,就是一句话:把两个对象的函数相关(往往是设置同一个值),而不用考虑两个对象之间的关系,而且可以一对多发信号.缺点是,事先要规定哪些函数可以信号,哪些函数是槽.而且执行内容貌似有点单调. 这一机制号称先进,但Delphi里其实也有,而且应该更强大,因为信号源可以任意指定.而且设计时可以,动

Qt信号槽中槽函数为虚函数的一些感想

有时候,在写connect的时候会去犹豫一个问题----我的槽函数到底需不需要为虚函数.这个问题在我每次写connect的时候我都会反问自己,因为确实,如果你不去深究里面的moc,你发现不了太多问题. 比如有这么一个demo. #include <QApplication> #include <QObject> #include <QDebug> class Test : public QObject { Q_OBJECT public: void onEmit() {

QT 信号槽 异步事件驱动 单线程 多并发

利用好Qt 模块的异步信号槽,单线程同样可是实现很强悍的的并发能力.应付正常的功能是足够的. 需要注意的是:该模式本质上为 单线程 事件驱动异步模式,所以需要做的事优化你的业务代码构架以应付性能与并发压力需求. (当然能够实现异步事件驱动,在Qt模块内部还是启动了更多的子服务线,但在用户代码层面确实单线程模式). 案例场景: 客户端程序,连接多服务端,接收服务端的上传数据. 20个TCP服务端启动.客户端APP逐个建立连接: 单个服务端上传吞吐:一个PDU 大小: 512*2*20字节    上

Qt 信号槽如何传递参数(或带参数的信号槽)

                                信号槽如何传递参数(或带参数的信号槽) 利用Qt进行程序开发时,有时需要信号槽来完成参数传递.带参数的信号槽在使用时,有几点需要注意的地方,下面结合实例进行介绍. 第一点:当信号与槽函数的参数数量相同时,它们参数类型要完全一致. signals: void emitdata(int); private slots: void getdata(int); // 具体实现 qDebug() << "int : "

[Qt] Qt信号槽

信号槽是Qt的核心机制之一,信号槽简单的来说就是两个对象及两个对象问答行为. 例如:发送者(Sender)发出信号(SIGNAL).接收者(Receiver)做出反应(SLOT).利用connect函数将这两个对象及其行为联系起来. 一个简单的例子如下,创建一个button,Clicked button make app quit. #include <QApplication> #include <QPushbutton> int main(int argc, char* arg

C++模板类与Qt信号槽混用

一.正文 目前正在做一个视频处理相关的项目.项目的技术栈是这样的,UI层采用Qt来实现基本的数据展示和交互,底层音视频采用的是一套基于FFmpeg的视频处理框架.这是一套类似Microsoft Media Foundation的处理框架,采用管道流进行架构,解复用.解码.复用.编码及用户自定义操作都采用Filter组件来实现,灵活度和可扩展性都比较好.(基本上常用音视频处理框架都采用了这一架构,如Microsoft Media Foundation, DirectShow Filter, gst

QT信号槽connect的第五个参数

用过QT的小伙伴都知道连接信号槽的connect方法,但是这个方法有第五个参数,一般都是用的默认的 connect(th,SIGNAL(started()),tmpmyobject,SLOT(showID())); 今天给大家讲讲第五个参数的用法 1.Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定.如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型.如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConne