Qt:使用QEventLoop等待信号,如果信号过早发射怎么办?

3

我正在开发一个小型客户端-服务器应用程序。客户端发送查询请求并需要等待答案。我认为最方便的方法是使用QEventLoop:

connect(&parser, SIGNAL(answer_received()), this, SLOT(react_to_answer()));
...
...
QEventLoop loop;
connect(&parser, SIGNAL(answer_received()), &loop, SLOT(quit()));
this.sendQuery();
loop.exec();

这个方法目前对我有效,但如果answer_received()信号在调用loop.exec()之前非常快地被触发,会发生什么呢?我的应用程序会一直卡在QEventLoop中吗?

谢谢!


1
在几乎所有的事件驱动GUI系统中(包括浏览器中的JavaScript和Windows API),事件不是异步发生的,而只有在代码明确等待时才会发生。当代码执行其他操作时,事件可能会排队等待。如果Qt在这方面有所不同,我会非常惊讶。 - Cheers and hth. - Alf
@Cheersandhth.-Alf Qt信号不是事件,通常最终会以同步直接调用的形式结束。 - Benjamin T
1
@Velkan 调用 QEventLoop::exec() 会将 QEventLoopPrivate::exit 设置为 false,因此调用 QEventLoop::quit() 并不能阻止事件循环的运行。 - Benjamin T
@BenjaminT:虚假二元论。 - Cheers and hth. - Alf
@Cheersandhth.-Alf 不是那样的虚假,否则你的第一条评论就会成立。Qt 中没有任何东西可以阻止在 sendQuery() 调用期间调用 quit() 槽,特别是因为连接更像是简单的函数调用而不是事件。为了确保在 loop.exec() 之后调用 quit(),必须以某种方式强制通过 Qt 事件系统进行,例如显式指定连接是排队的。 - Benjamin T
1个回答

4
考虑到你的代码,唯一可能出现问题的情况是在调用loop.exec()之前发射了answer_received()信号。也就是说,在调用this.sendQuery();期间发射了answer_received()信号。
在你的情况下,这不太可能发生,因为你依赖于服务器/客户端交互,很可能使用了QNetworkAccessManager或某种类型的QSocket。如果是这种情况,QNetworkAccessManager/QSocket将不会在进入事件循环之前发射readyRead()finished()信号。
然而,在更一般的情况下,如果answer_received()可以从this.sendQuery()中发射,您需要更改您的代码:
  1. You could make the connection between to be queued. This way even if answer_received() is emitted during this.sendQuery() the slot will not be called until you enter the event loop.

    connect(&parser, SIGNAL(answer_received()), &loop, SLOT(quit()), Qt::QueuedConnection);
    
  2. You could ensure that answer_received() is never emitted during this.sendQuery(). On way to do so is to use 0ms QTimer, which will trigger as soon as all events in the event queue are processed, i.e during loop.exec()

    Replace:

    emit answer_received();
    

    By:

    QTimer::singleShot(0, this, *receiver, &MyClass::answer_received);
    

非常感谢,您的回答很有道理。我有您在第一句中提到的担忧。 - Horst

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