Qt 信号(QueuedConnection 和 DirectConnection)

Posted

技术标签:

【中文标题】Qt 信号(QueuedConnection 和 DirectConnection)【英文标题】:Qt signals (QueuedConnection and DirectConnection) 【发布时间】:2013-02-09 16:28:48 【问题描述】:

我遇到了 Qt 信号问题。

我不明白DirectConnectionQueuedConnection 是如何工作的?

如果有人能解释何时使用其中的哪一个(示例代码将不胜感激),我将不胜感激。

【问题讨论】:

见***.com/questions/11230080/…和qt-project.org/doc/qt-4.8/threads-qobject.html 您对这些具体有哪些不了解的地方?您的问题归结为“信号和插槽如何工作”,没有那个,这有点宽泛,文档很好地涵盖了。 嗯..据我了解,当发送方和接收方处于不同线程时,应使用 QueuedConnection。例如,我有 GUI 线程(主线程)和具有信号的新线程(pThread),例如 void doSomething();接收器是主要的 GUI 线程。所以,我必须使用 QueuedConnection 没关系我会在哪里调用它? (在 GUI 线程或新线程中,连接命令)谢谢.. 阅读第一条评论中的文档。除非您尝试做一些非常具体的事情并且您完全了解风险,否则根本不要指定连接模式。默认将使用正确的模式(直接用于线程内,排队用于线程间)。 【参考方案1】:

除非您使用具有不同线程关联性的对象,否则您不会看到太大的差异。假设您有 QObjects AB 并且它们都附加到不同的线程。 A 有一个名为 somethingChanged() 的信号,B 有一个名为 handleChange() 的槽。

如果你使用直接连接

connect( A, SIGNAL(somethingChanged()), B, SLOT(handleChange()), Qt::DirectConnection );

handleChange() 方法实际上会在A 的线程中运行。基本上,就好像发出信号“直接”调用 slot 方法一样。如果B::handleChange() 不是线程安全的,这可能会导致一些(难以定位)错误。至少,您错过了额外线程的好处。

如果您将连接方法更改为Qt::QueuedConnection(或者,在这种情况下,让 Qt 决定使用哪种方法),事情会变得更有趣。假设B 的线程正在运行一个事件循环,发出信号将向B 的事件循环发布一个事件。事件循环将事件排队,并最终在控制权返回时调用 slot 方法(它是事件循环)。这使得在 Qt 中处理线程之间的通信变得非常容易(再次假设您的线程正在运行它们自己的本地事件循环)。您不必担心锁等问题,因为事件循环会序列化插槽调用。

注意:如果您不知道如何更改 QObject 的线程关联,请查看QObject::moveToThread。这应该让你开始。

编辑

我应该澄清我的开场白。如果您指定一个排队的连接,它确实会有所不同——即使对于同一线程上的两个对象也是如此。该事件仍被发布到线程的事件循环中。因此,方法调用仍然是异步的,这意味着它可以以不可预知的方式延迟(取决于循环可能需要处理的任何其他事件)。但是,如果您不指定连接方法,则直接方法会自动用于同一线程上的对象之间的连接(至少在 Qt 4.8 中是这样)。

【讨论】:

@Jacob 那么像普通函数一样直接调用handleChange()而不是直接连接情况下的信号槽混乱不是更好吗? Cool_Coder:通过 sig/slot 连接调用方法的好处是您不必将对象 B 的知识构建到 A 中 - 您可以让两个类的用户确定当somethingChangedA 发出时应该发生什么。这有意义吗? 如果我不指定Qt::QueuedConnection 作为参数会发生什么?我问这个的原因是在许多地方指定Qt::QueuedConnection 或直接是一个可选参数。这是否意味着 Qt 会自行决定是“将信号加入队列还是直接调用”? @Game_Of_Threads :默认参数是Qt::AutoConnection。如果发射器和接收器在同一个线程中,则使用DirectConnection。否则,使用QueuedConnection @JacobRobbins 请您解释一下为什么我们在使用Qt::QueuedConnection 时不需要使用锁或同步?即使两个线程共享一些数据(两者都可以读取或写入)?或者当线程A通过连接到线程B中的插槽的信号的参数传递一些数据时?【参考方案2】:

除了 Jacob Robbins 的回答:

“除非您使用具有不同线程亲和性的对象,否则您不会看到太大差异”这一说法是错误的

向同一线程内的直接连接发出信号将立即执行该槽,就像一个简单的函数调用一样。

向同一线程内的排队连接发出信号会将调用排队到线程事件循环中,因此执行将总是发生延迟。

QObject based class has a queued connection to itself

【讨论】:

我现在完全理解信号和插槽了,谢谢你提到它。 好点。我总是让 Qt 选择连接方法,除非我有充分的理由选择其中一种……所以我得到了直接连接。当对象在同一个线程上时(使用 4.8)。如果您强制使用排队连接,那肯定会有所作为。我编辑了我的答案以澄清。【参考方案3】:

Jacob 的回答很棒。我只想在嵌入式编程中添加一个比较示例。

来自嵌入式 RTOS/ISR 背景,了解 Qt 的 DirectConnection 与 ISR 的抢占行为和 Qt 的 QueuedConnection 与任务之间的 RTOS 中的队列消息的相似性是有帮助的。

旁注:来自嵌入式背景,我很难不定义编程中的行为。我从不将论点保留为 Auto,但这只是个人意见。我更喜欢明确写出所有内容,是的,这有时会变得很困难!

【讨论】:

我不完全相信明确选择直接连接和排队连接总是更好。在事物快速发展的环境中,随着线程模型的发展,返回并调整这些所需的时间将使直接连接最终可能被指定为跨线程信号并最终导致间歇性的非零概率在某处的某台机器上崩溃(更快或更慢的处理器上的竞争条件等)。只是说它在开发过程中增加了风险。 我尊重这个立场,但我的观点是,我想知道在编写模块后行为是否发生了变化,对我来说,最简单的方法是让编译器在编译期间发出信号。通过明确定义变量,一旦行为发生变化,我就会知道。所以是的,它可能会减慢开发速度,但我更喜欢它几个月后发现行为已经改变并且不适用于原始设计。

以上是关于Qt 信号(QueuedConnection 和 DirectConnection)的主要内容,如果未能解决你的问题,请参考以下文章

Qt::ConnectionType(信号与槽的传递方式)

通过 QT 的信号/槽系统作为参数传递通用异常

qt 线程与ui线程同步

Qt信号和槽对值传递参数和引用传递参数的总结

我可以让 Qt 分析信号槽执行持续时间吗?

2020-11-20 Qt事件循环