注:此文是站在Qt5的角度说的,对于Qt4部分是不适用的。git
1)Qt信号槽给出了五种链接方式:github
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的自动选择,直接分析那两种也就好了。函数
发出信号,调用槽的方式也能够简单的分为两种:同步调用和异步调用post
同步调用:发出信号后,当前线程等待槽函数执行完毕后才继续执行。性能
异步调用:发出信号后,当即执行剩下逻辑,不关心槽函数何时执行。测试
因此有下表:spa
线程/模式 | DirectConnection | QueuedConnection | BlockingQueuedConnection |
相同线程 | 直接调用,同步调用。 | 经过事件进行队列调用。异步调用. | 不可用 |
不一样线程 | 直接调用。同步调用。 槽函数在发出信号的线程执行。线程 有线程安全隐患。
|
经过事件进行队列调用。异步调用. 槽函数在对象所在的线程执行。 线程安全。 |
经过事件进行阻塞调用。同步调用。 槽函数在对象所在的线程执行。 线程安全。 |
先说基本原则:
槽函数开始调用的顺序和链接的顺序是一致的。
可是,上面也说了,有同步调用和异步调用。
对于同步调用,你观察的结果和基本原则同样。
可是对于异步调用,可能你最早链接的它,可是可能其余都执行完毕了,可是其还没执行。是由于对于异步调用:是开始调用的时候,生成一个须要调用这个 函数的事件,而后放到事件队列里。而后当即返回,去执行调用其余槽函数或者槽函数都执行了,不关心槽函数的执行状态的。等到事件队列里任务轮到此事件再去 调用。
大都说Qt信号槽不能使用返回值。其实不不许确的,Qt5中,信号槽是有返回值的。只是Qt的一个信号能够链接多个槽,还有同步调用和异步调用的问题,没发支持的很好,因此,返回值虽有,但只是鸡肋。
先说下返回值的规则把:
同步调用才有返回值,异步调用的返回值永远为返回值类型默认构造函数出来的。
链接的多个槽都返回值,那么结果是最后调用(链接)的那个。
也就是说对于QueuedConnection链接的信号槽,永远只是返回返回类型的默认构造函数的。对于AutoConnection链接的,若是发出信号的线程和槽函数线程不一样亦然。
测试小例子地址:https://github.com/dushibaiyu/DsbyLiteExample/tree/master/QtSignalsSlotTest
由于一个信号能够链接多个槽函数,若是参数是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 *,最好不要同时链接多个槽。
对于同步调用:是一个接着一个调用的,执行顺序相似上面,因此值也是每次调用也会变化的。
对于异步调用:其内容确实不肯定的,由于异步调用的时间是不可控的。若是还有跨线程相关,则还有线程安全问题。
注:仅仅代码层进行的理论分析,非实际测试,不严谨,不权威。
关于信号槽(不少吐槽Qt就是说的这个):
(1)Qt4语法的,都说是匹配字符串,其实只是连接信号槽的用的匹配字符串 的方法,经过字符串找到信号和槽在QMeatObject里存的索引位置int类型,还有槽函数的索引,而后调用的时候经过索引号用switch去区分的 发射的那个函数,而后取出对应的连接槽的list,循环检测槽函数的参数是否匹配,而后调用槽函数。。这个连接时会耗时查找,可是你能有多少信号?这个链 接也耗时很少,调用的时候耗时主要就是在参数匹配上了。
(2)Qt5 语法的,Qt5 的槽函数连接和执行是基于模板实现的,函数对象。信号和槽的参数问题是编译时检查的,执行效率更高,可是编译就慢点了。连接时也是经过信号的地址找到其的 信号索引,至于槽函数直接是生成一个函数对象的,而后调用的时候也是先switch找到发射的信号,取出list,而后逐个调用其储存的函数对象,因此对 于Qt5 语法的信号槽,调用性能损失几乎能够说无的。
(3)连接的信号槽的时候,Qt::UniqueConnection的连接方式会对已经连接过的此先好的槽函数进行遍历,会有连接时的损失。其余连接的损失就在上面说过了。
(3)在信号槽调用的时候,还有一些连接方式和线程的判断和为了安全问题的锁操做。关于这个就还涉及到调用槽函数的线程问题。
对于同线程直接调用,较函数对象直接调用的损失,就只有连接方式和线程的判断的几个if 分支和 锁的操做。
对于线程间通信的调用,跨线程。信号槽内部也是经过Qt事件循环机制实现的,跨线程就不是时时调用了,主要是安全了,对于性能有没有损失无法评论的。对于跨线程阻塞的调用,这个也是事件实现,只是但发射信号的线程会阻塞,这个找不到对应的直接调用的比较,也很差说。
关于信号槽Qt是做何不少方便使用和安全调用,较之函数指针,性能会有损失,可是也没损失多少的。对于函数对象调用,Qt5语法的调用,几乎是不损失什么的。
注:此文是我的根据文档,源码和本身写小例子测试得出的总结,若有错误请您指出。