为什么即使早期的槽仍在处理中,socket::readyRead() 的新信号仍会被执行?

6
根据以下帖子,只有当前执行的 slot 完成后,发出的信号才会被处理。
等待 Qt SLOT 执行完毕 我有一个基于 SSL 套接字的客户端-服务器通信应用程序,它是单线程的。
connect(socket, &QSslSocket::readyRead, [&]() { myObject.Read(); });

客户端和服务器相互发送一些自定义消息。每当任何一方发送或接收消息时,它们都会发送 ACK 字节 (00)。
大多数情况下,我发现当 Read() 在执行中间时,下一个 readyRead() 被调用了!我在 myObject->Read() 的开始和结束处放置了调试语句。它们证实了开始的调试被再次调用。断点也有同样的现象。
当接收到太多数据时,会创建太多 Read() 的递归堆栈帧。 它要么使应用程序GUI变慢,要么崩溃。
通常,当客户端尝试作为 myObject->Read() 的一部分发送 ACK 时,就会发生这种递归。此时不经意间发出并处理了 readyRead()。然而,前一个信号的槽仍在处理中。 问题:
  • Qt框架是否可能在槽仍处于中途(单线程)时提供信号?
  • 如何解决此套接字特定的情况?
注意:
- 对于单个线程,默认情况下,Qt::ConnectionTypeDirectConnection。我已经尝试使用 QueuedConnection,但结果是相同的。
- myObject.Read() 是相当复杂的,并且有许多其他函数调用。如果这是问题的原因,请告诉我应该查找什么。写出它的实际代码是不切实际的。

1
这可能会有所帮助:https://dev59.com/-WXWa4cB1Zd3GeqPNHnh - Amol Saindane
@G.M.,我已经尝试过了,但并没有任何优势。然而,很明显Socket::readyRead()在任意时间被调用。这没关系,但是不知怎么的,它会立即处理,即使之前的SLOT还没有完成。有没有办法检查事件循环是否在中间空闲了(虽然我认为这是不可能的)。 - iammilind
1
@iammilind:正如你所说,在myObject.Read()中有很多函数调用,你可以使用QtConcurrent::run函数。它允许你异步运行一个函数。链接- http://doc.qt.io/qt-4.8/qtconcurrentrun.html - Amol Saindane
@SamuraiJack,谢谢,我可以尝试一下。目前为止,我注意到我有很多sqlite操作,这使得GUI运行缓慢。如果我禁用它们,则GUI会快得多。我能否使用QtConcurrent::run()功能来执行这些单个的sqlite操作(即INSERT,UPDATE)?它会每次创建一个线程(耗时),还是保留一个线程池以供将来使用以获得更好的性能?请建议。 - iammilind
@iammilind:我从未尝试过针对sqlite的特定操作,但你可以试一试。根据文档,该函数在单独的线程中运行,但如何管理其中的sqlite操作取决于你。 - Amol Saindane
显示剩余4条评论
1个回答

2
readyRead()的递归调用是由于事件循环在中间被释放导致的。以下函数会导致事件循环被释放:
  1. QCoreApplication::processEvents()
  2. SslSocket::flush()
第一个函数很容易理解,因为它就是为此而设计的。但是第二个flush()却让人大吃一惊。根据文档,并没有说明这一点。至少在我的调试中,每当调用flush()时,随后的readyRead()都会被调用和处理。在问题中也可以看到这一点。 processEvent()旨在使GUI在数据高负载期间更加响应。但似乎我们需要为此做出另一个选择。

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