Qt信号槽机制
Qt
1.Qt 信号槽机制
概念:
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。
槽的本质是类的成员函数,其参数可以是任意类型的。和普通C++成员函数几乎没有区别,它可以是虚函数;也可以被重载;可以是公有的、保护的、私有的、也可以被其他C++成员函数调用。唯一区别的是:槽可以与信号连接在一起,每当和槽连接的信号被发射的时候,就会调用这个槽。
自定义信号槽注意事项:
(1)发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
(2)使用 signals 标记信号函数,信号是一个函数声明,返回 void,不需要实现函数代码;
(3)槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
(4)使用 emit 在恰当的位置发送信号;
(5)使用QObject::connect()函数连接信号和槽;
(6)任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数。
信号槽的多种用法:
(1)一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。
(2)多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。
(3)一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。
(4)槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。
(5)使用Lambda 表达式
在使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。
2. QT信号槽机制的优缺点
(1)问题:
为什么Qt使用信号与槽机制而不是传统的回调函数机制进行对象间的通信呢?
回调函数的本质是“你想让别人的代码执行你的代码,而别人的代码你又不能动”这种需求下产生的。
回调函数是函数指针的一种用法,如果多个类都关注某个类的状态变化,此时需要维护一个列表,以存放多个回调函数的地址。对于每一个被关注的类,都需要做类似的工作,因此这种做法效率低,不灵活。
(2)解决办法:
Qt使用信号与槽机制来解决这个问题,程序员只需要指定一个类含有哪些信号函数、哪些槽函数,Qt会处理信号函数和槽函数之间的绑定。当信号函数被调用时,Qt会找到并执行与其绑定的槽函数。允许一个信号函数和多个槽函数绑定,Qt会依次找到并执行与一个信号函数绑定的所有槽函数,这种处理方式更灵活。
(3)优点:
Qt信号与槽机制降低了Qt对象的耦合度.
3. 多线程情况下, Qt中的信号槽分别在什么线程中执行, 如何控制?
通过connect函数的第五个参数connectType来控制。
connect用于连接qt的信号和槽,在qt编程过程中不可或缺。它其实有第五个参数,只是一般使用默认值,在满足某些特殊需求的时候可能需要手动设置。
Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。
4.Qt信号与槽使用不当,使程序崩溃
在调整程序的过程中出现APPCRASH等错误,可能由以下原因产生:
1.在槽函数中错误使用了锁,导致资源的死锁;
2.跨线程使用Qt信号和槽,信号发送时间间隔小于槽函数处理时间时,造成程序崩溃。
原因分析
跨线程使用Qt信号和槽时,connect默认是QueuedConnection,队列连接方式。
信号传递给槽函数的参数,分配内存后放入队列,如果槽函数处理不过来,就会造成队列不停增长,消耗的内存不停增加,最后程序崩溃。
处理方法
处理方法各种各样,原则是保证信号发送间隔不大于槽函数处理时间