Qt::QueuedConnection存在问题,信号在断开连接后仍被传递。

7

我刚刚发现了Qt 4.6中排队连接的有趣行为:

首先进行排队连接:

connect(someSender, SIGNAL(completed()), this, SLOT(handleCompletion()), Qt::QueuedConnection)

然后,某个发送者发送了信号:

emit completed()

在接收信号之前(当它在队列中时),我会断开与信号的连接:

disconnect(someSender, SIGNAL(completed()), this, SLOT(handleCompletion())

然而,handleCompletion插槽在下一个事件循环迭代中被调用。我可以通过在正确的位置使用someSender->blockSignals(true)来防止这种情况发生,但是这种做法感觉很糟糕,还有一些布尔标志可以禁用插槽的功能。

尤其是,我感到惊讶的是Qt文档中没有提到这种行为(至少我没有找到)。

最后的问题:有什么明智的方法可以避免这种情况发生吗?


我猜Qt :: QueuedConnection的实现方式是在接收方(事件循环)上排队,因此在断开连接(disconnect())之前发出的所有内容都应该进入该队列并保留在那里,直到处理完为止。 可能您希望在信号发射时同步调用disconnect()。否则,如果该信号将被处理或不被处理没有太大区别,因为handleCompletion()可以更早地完成旧事件,并且在您预期的情况下将处理completed()。也就是说,如果您没有指定线程交互的直接场景,则程序可以按其所需工作。 - ony
如果你想的话,可以去黑Qt代码,它写得非常好。 - Georg Schölly
1个回答

9
我认为Qt的行为是最直观的。
当您执行`emit completed()`时,信号立即激活或排队所有连接的插槽是有意义的。如果排队的插槽可以由于断开连接而被取消排队,那么就更难理解,并且更容易出现竞争条件。此外,请考虑更复杂的情况:例如,如果断开连接会将其从队列中删除,则重新连接是否会将其放回?
如果它按照您的期望工作,那么在这个问题的位置上,将会是“Qt :: QueuedConnection存在问题,断开连接后信号未传递”。
至于避免这种情况的明智方法:您应该重新设计代码,以使发送方和接收方都不必关心信号是直接连接还是排队连接。我建议发送方和接收方都不要调用`QObject::connect`,而是由第三个类完成。这并不能完全解决您的问题,这取决于您在何处以及为什么执行`disconnect`。

好的,听起来很合理。如果支持“取消排队断开连接”,我仍然会觉得它是有用的。是的,除了缺乏空闲时间(可能还有技能不足)之外,没有任何事情阻止我自己添加它。 - user110418
如果您在断开连接时销毁了某些资源,并且假定这些资源始终可访问,则会出现问题。在这种情况下,您必须适当地保护您的插槽。我遇到了这个问题,这就是为什么我来这里的原因。 - Haiyuan Li

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接