信号槽 与事件区别

Posted 朱诸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了信号槽 与事件区别相关的知识,希望对你有一定的参考价值。

转自网络

仔细来看,事件与信号其实并无多大差别,从我们对其需求上来说,都只要能注册事件或信号响应函数,在事件或信号产生时能够被通知到即可。但有一项区别在 于,事件处理函数的返回值是有意义的,我们要根据这个返回值来确定是否还要继续事件的处理,比如在QT中,事件处理函数如果返回true,则这个事件处理 已完成,QApplication会接着处理下一个事件,而如果返回false,那么事件分派函数会继续向上寻找下一个可以处理该事件的注册方法。信号处 理函数的返回值对信号分派器来说是无意义的。

    另外还有一个需要我们关注的问题是事件和信号处理时的优先级问题。在QT中,事件因为都是与窗口相关的,所以事件回调时都是从当前窗口开始,一级一级向上 派发,直到有一个窗口返回true,截断了事件的处理为止。对于信号的处理则比较简单,默认是没有顺序的,如果需要明确的顺序,可以在信号注册时显示地指 明槽的位置。
    在QT中,事件使用了一个事件队列来维护,如果事件的处理中又产生了新的事件,那么新的事件会加入到队列尾,直到当前事件处理完毕后, QApplication再去队列头取下一个事件来处理。而信号的处理方式有些不同,信号处理是立即回调的,也就是一个信号产生后,他上面所注册的所有槽 都会立即被回调。这样就会产生一个递归调用的问题,比如某个信号处理器中又产生了一个信号,会使得信号的处理像一棵树一样的展开。

评价:实际上信号-槽也可以异步的,这个bool connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection )
里面的Qt::AutoCompatConnection,可以根据实际情况确定

 

 所有使用了信号和槽的类都必须包含 Q_OBJECT 宏,而且这个类必须从QObject类派生(直接或者间接派生)出来,
    当一个signal被emit出来的时候,链接到这个signal的slot会立刻被调用,就好像是一个函数调用一样。当这件事情发生的时 候,signal和slot机制与GUI的事件循环完全没有关系,当所有链接到这个signal的slot执行完成之后,在 emit 代码行之后的代码会立刻被执行。当有多个slot链接到一个signal的时候,这些slot会一个接着一个的、以随机的顺序被执行。
    Signal 代码会由 moc 自动生成,开发人员一定不能在自己的C++代码中实现它,并且,它永远都不能有返回值。
    Slot其实就是一个普通的类函数,并且可以被直接调用,唯一特殊的地方是它可以与signal相链接。
    C++的预处理器更改或者删除 signal, slot, emit 关键字,所以,对于C++编译器来说,它处理的是标准的C++源文件。

    signal/slot 在底层会使用三种方式传递消息。bool QObject::connect ( const QObject * sender, const char * signal, const QObject * receiver, const char * method, Qt::ConnectionType type = Qt::AutoCompatConnection )
最后一个参数是就是传递消息的方式了,有四个取值:

1.Qt::DirectConnection
When emitted, the signal is immediately delivered to the slot.
假设当前有4个slot连接到QPushButton::clicked(bool),当按钮被按下时,QT就把这4个slot按连接的时间顺序调用一遍。显然这种方式不能跨线程(传递消息)。

2.Qt::QueuedConnection
When emitted, the signal is queued until the event loop is able to deliver it to the slot.
假 设当前有4个slot连接到QPushButton::clicked(bool),当按钮被按下时,QT就把这个signal包装成一个 QEvent,放到消息队列里。QApplication::exec()或者线程的QThread::exec()会从消息队列里取消息,然后调用 signal关联的几个slot。这种方式既可以在线程内传递消息,也可以跨线程传递消息。
3.Qt::BlockingQueuedConnection
Same as QueuedConnection, except that the current thread blocks until the slot has been delivered. This connection type should only be used for receivers in a different thread. Note that misuse of this type can lead to dead locks in your application.
与Qt::QueuedConnection类似,但是会阻塞等到关联的slot都被执行。这里出现了阻塞这个词,说明它是专门用来多线程间传递消息的。
4.

Qt::AutoConnection
If the signal is emitted from the thread in which the receiving object lives, the slot is invoked directly, as with Qt::DirectConnection; otherwise the signal is queued, as with Qt::QueuedConnection.
这种连接类型根据signal和slot是否在同一个线程里自动选择Qt::DirectConnection或Qt::QueuedConnection

这样看来,第一种类型的效率肯定比第二种高,毕竟第二种方式需要将消息存储到队列,而且可能会涉及到大对象的复制(考虑sig_produced(BigObject bo),bo需要复制到队列里)。

 

Qt的线程和 signal-slot

再问coredump或者QT高手们一个问题。QT里用线程,主线程是GUI,次线程是工作线程,如果次线程里也用消息循环(exec),即,主/次间用signal-slot通讯来实现同步,在这种情况下, 1.主线程会被次线程block吗? 2.如果在单cpu里会被block吗,如果次线程里不断循环,低层会自动分配时间片给主线程吗? 3.这种方法和直接调用次线程中的方法,用mutex之类的来实现类似的循环,效率差别大吗?记得QT里讲过signal-slot只是多几个函数调用的差别。 呵呵,问的不太清楚,主要就是想问一下,主次线程中怎样通讯比较有效。 谢谢阿。

QThread 现在默认状态下就是有一个消息循环的,而且鼓励使用线程独立的消息循环 1.主线程会被次线程block吗? 不会,QObject::connectd的最后一个参数默认使用Qt::AutoConnection, 在多线程环境下,会自动选择Qt::QueuedConnection方式,除非强制使用Qt::BlockingQueuedConnection或者 Qt::DirectConnection, 否则线程不会block. 2.如果在单cpu里会被block吗,如果次线程里不断循环,低层会自动分配时间片给主线程吗? 线程和CPU个数没有关系,当然CPU多了,运行会更加流程,线程的资源分配(时间片)是由操作系统管理的,在各个系统上都是如此 3.这种方法和直接调用次线程中的方法,用mutex之类的来实现类似的循环,效率差别大吗? 执行效率上很难讲,但是mutex肯定是带来了开发效率的降低,你得小心翼翼地处理好各种临界资源的锁问题 4. 记得QT里讲过signal-slot只是多几个函数调用的差别 是的,signal-slot的这种轻微的效率损失在绝大多数情况下不是问题,除非过度使用,这一般是设计上的错误导致的 分享两篇文章,讲QThread和Qt的signal-slot的: 1篇是qt labs的博客上一篇 You are doing it wrong...(http://labs.trolltech.com/blogs/2010/06/17/youre-doing-it-wrong/), 这篇文章很有意思,其实很多人不知道原来QThread还可以这样用。 另外一篇是关于怎么利用Qt的signal-slot机制和QThread进行完全的lock free编程(http://thesmithfam.org/blog/2009 ... ti-threading-in-qt/)

多谢多谢,再学习去。 之所以问,是因为,我做了一个下载googlemap的程序,开始下载线程后,GUI就不动了,直到下载结束。虽然操作系统会分配时间,但是如果下载线程循环,而且优先级相同的话,是不是还是有影响?

我再看看哪里出问题了。 刚看了你上面的两个链接,真是切中要害阿,呵呵,一直就在琢磨这个一段时间了。 总结一下理解,看看对不对。 第一篇里,QThread里默认的run()已经启动了消息循环exec(). 当需要新线程时,应该,直接生成QThread, start()。至于功能,应该放在另外的类里,在调用start前设置好signal-slot,再moveToThread到 QThread中。 不用sunbclass QThread。 我一直或者重新实现run(),在run()里生成所有object, signal-slot, 或者如文中所说的QThread.moveToThread(QThread). 这两种都有不少问题,破坏OO的概念。 很清楚。

第二篇里讲了用线程间的signal-slot来同步,正好回答了之前的疑问,不过他使用qthread的方法恰恰违背了第一篇的思想。

引用:
原帖由 GPS 于 12-7-2010 12:40 发表 技术分享 我再看看哪里出问题了。 刚看了你上面的两个链接,真是切中要害阿,呵呵,一直就在琢磨这个一段时间了。 总结一下理解,看看对不对。 第一篇里,QThread里默认的run()已经启动了消息循环exec(). 当需要新线程时 ...
另一种方式也不错,有些情况下必须subclass QThread, 错的不是是否subclass,错在:subclassing QThread, adding signal and slot code to that subclass, and then calling moveToThread(this);
引用:
To set the record straight: I’m not saying sub-classing QThread is wrong. This is also how Java does it (sort of), and many people have been doing this with Qt for a long time. What I’m saying is “wrong” is subclassing QThread, adding signal and slot code to that subclass, and then calling moveToThread(this); in the constructor of that subclass. This causes people lots of confusion from my experience and observation on the #qt channel and the qt-interest mailing list. And yes, the current docs are still a bit lacking, something that I am fully aware of (and take responsibility for). I’m planning on sitting down and fleshing them out, showing both ways of using QThread (with and without subclassing).
在Qt的编程模型里,thread和signal- slot都是实现异步编程的手段,signal-slot的背后是消息循环,而每个thread都是一个独立的消息循环,在早期版本,qt线程间无法进行 signal-slot消息传递,这就导致了各个消息循环都是互相独立的loop, 通讯的唯一方式只剩下global state+ mutex lock。在4.x 之后,signal-slot已经能够和thread融洽相处了。 这样,理想的QT编程模型就变成了独立的一个个任务,各自 使用自己的thread和thread内部消息循环,在需要互相通讯的时候,使用signal-slot, 这些signal和slot就是明确定义的消息接口,除此之外,最好不共享其它状态。Qt程序员有点像转盘子的杂技演员,每个盘子都独自转动,整个系统由 很多旋转的盘子组成,这些盘子可以共享杂技演员一个手,也可以是另一只手甚至不同的杂技演员来控制(多CPU)

以上是关于信号槽 与事件区别的主要内容,如果未能解决你的问题,请参考以下文章

Qt的信号槽机制可以保障线程安全吗

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

Qt信号与槽函数问题

PyQt5信号与槽详解

webrtc 信号槽实现分析

QT开发(十三)——QT信号与槽机制