1.应用于对象之间的通信
2.类似观察者模式(信号发送者->被观察者,信号接收者->观察者)
3.与回调不同:回调:通过一个函数指针链表
信号槽:减少耦合,谁先连接先执行谁
4.第5个参数:第5个参数代表在哪个线程
(1):自动连接(AutoConnection),默认的连接方式,如果信号与槽,也就是发送者与接受者在同一线程,等同于直接连接;如果发送者与接受者处在不同线程,等同于队列连接。
(2):2)直接连接(DirectConnection),当信号发射时,槽函数立即直接调用。无论槽函数所属对象在哪个线程,槽函数总在发送者所在线程执行,即槽函数和信号发送者在同一线程
(3):队列连接(QueuedConnection),当控制权回到接受者所在线程的事件循环时,槽函数被调用。槽函数在接受者所在线程执行,即槽函数与信号接受者在同一线程
(4):定队列连接(BlockingQueuedConnection),槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
(5):单一连接(UniqueConnection),这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接
5.槽函数何时执行:
每个线程均有自己的消息事件循环队列
直连方式,是所谓立即执行,就是函数直接调用。
队列方式,不会立即执行,分单/多线程情况:
a.单线程(发送、接收同一线程):消息放入消息队列。线程进入消息队列时,依次执行队列上消息对应槽函数。
b.跨线程(发送、接收不同线程):消息放入接收者线程队列。接收者线程运行时(可能阻塞、睡眠、或退出),进入它的消息队列后,依次执行队列上的消息。这也意味着,同一个槽函数不会重入,不用考虑重入互斥访问,因为都在队列上排队等待依次执行(注意不要乱用postEvents函数,后续我们有机会单独讲一讲该问题)。
6.QT多线程变成注意点:
(1).QT主线程不能阻塞。因为UI在主线程中,阻塞则界面卡死。使用while()QCoreApplication::processEvents(); qapp->processEvents()替换睡眠操作。
(2).非主线程不能操作UI控件,否则QT崩溃。这也是要分离界面与逻辑的重要原因
(3).父子QObject对象,必须在同一个线程;不同线程的对象,不能是父子关系。
否则会报错,或者产生未知情况
(4).官方优先推荐使用moveToThread方式,其次使用继承QThread方式。
(5).注意槽函数是在线程中执行。如果执行线程睡眠、阻塞,槽函数没有机会执行。如果你的槽函数要快速响应,不要让它在可能阻塞或睡眠的线程中
(6).要注意volidate修饰共享变量、要注意加锁。不同的锁行为会导致线程不同状态,得根据线程业务状态去考虑用什么锁
(7).活用慎用processEvents,线程(包括主线程和其它线程)执行很繁重的计算任务中,为防止消息事件一直无机会处理,则在函数中手动调用processEvents,让消息事件有机会更新。界面不会假死。另一方面,槽函数中不可调用processEvents,因为这会导致“中断”当前槽函数,进而去执行消息队列中后续的槽函数,可能会引发同一槽函数重入问题,将编程问题复杂化。