【问题标题】:Blocked waiting for a asynchronous Qt signal阻塞等待异步 Qt 信号
【发布时间】:2011-04-03 03:42:35
【问题描述】:

我知道,有一些与以下类似的问题,但我找不到对我有帮助的具体答案。所以这是我的问题:

我正在开发一个在启动时执行一些 gui 初始化的应用程序。我必须做的一件事就是打电话

NetworkConfigurationManager::updateConfigurations ()

这是一个异步调用,完成后会发出updateCompleted() 信号。问题是,我所有其他的 gui 初始化都必须等到 updateConfigurations() 完成。

所以我能做的就是这样:

MyApp::MyApp(QWidget *parent) : ....
{
   doSomeInits();
   //Now connect the signal we have to wait for
   connect(configManager, SIGNAL(updateCompleted()), this, SLOT(networkConfigurationUpdated()));
   configManager->updateConfigurations(); //call the async function
}

void MyApp::networkConfigurationUpdated()
{
   doSomething();
   doRemainingInitsThatHadToWaitForConfigMgr();
}

拆分初始化对我来说似乎不是一个好方法。我认为它使代码更难阅读 - 初始化应该保持在一起。另一件事是:因为updateConfiguration()异步的,用户将能够使用GUI,它还没有给他任何信息,因为我们正在等待updateCompleted()

那么有没有办法在应用程序继续之前等待updateCompleted() 信号?

喜欢:

MyApp::MyApp(QWidget *parent) : ....
{
   doSomeInits();
   //Now connect the signal we have to wait for
   connect(configManager, SIGNAL(updateCompleted()), this, SLOT(doSomething()));
   ???? //wait until doSomething() is done.
   doRemainingInitsThatHadToWaitForConfigMgr();
}

在某些 API 中,有异步函数的阻塞替代方案,但在这种情况下没有。

感谢您的帮助。谢谢!

【问题讨论】:

    标签: c++ qt qt4


    【解决方案1】:

    这样做的方法是使用嵌套事件循环。您只需创建自己的 QEventLoop,将您想要等待的任何信号连接到循环的 quit() 插槽,然后将 exec() 连接到循环。这样,一旦调用了信号,就会触发QEventLoop的quit()槽,从而退出循环的exec()

    MyApp::MyApp(QWidget *parent) : ....
    {
        doSomeInits();
        {
            QEventLoop loop;
            loop.connect(configManager, SIGNAL(updateCompleted()), SLOT(quit()));
            configManager->updateConfigurations(); 
            loop.exec();
        }
        doReaminingInitsThatHadToWaitForConfigMgr();
    }
    

    【讨论】:

    • 是的,这行得通,它是“阻塞等待而不阻塞 UI”的标准模式。如果可能的话,我会避免它。嵌套事件循环可能会导致严重的问题,例如用户在 loop.exec() 时执行随机不可预见的事情,退出应用程序等,在 exec() 返回后使应用程序处于不可预见的不一致状态。或者:(用户)事件打开另一个本地事件循环,等待需要第一个循环完成的事件,从而导致一种准死锁。这就是为什么我建议使用一个插槽并在那里继续,即使这更冗长。
    • 首先:感谢您的帮助。您的回答是正确的,尽管它不适用于我的解决方案。我想我有弗兰克提到的问题之一,因为我的循环无法退出。尽管如此,我还是在诺基亚 Wiki (wiki.forum.nokia.com/index.php/…) 中找到了您回答的证据。所以谢谢大家!
    • 根据 Qt/Nokia,您不应该使用 QEventloop,因为它会导致递归失控(请参阅developer.nokia.com/community/wiki/…)。这应该仅用于测试。还是我误读了这篇文章?
    • 这个答案对于编写自动化测试用例很方便,谢谢!
    • Frank 是对的——QEventLoop 方法很危险。我发现这很困难。我有一个应用程序需要以正确的顺序获取多个网页,并且逻辑在状态机中从一个函数跳到另一个函数非常讨厌,所以我尝试使用 QEventLoop。好吧,当我这样做时,如果用户在事件循环进行时移动窗口(或某些其他东西),则窗口焦点会丢失。整个应用程序将冻结。所有应用程序都会冻结!点击 alt-tab 是重新获得控制权的唯一方法,然后它会再次正常运行。所以是的......这不是一个好的解决方案。
    【解决方案2】:

    使用chalupanswer,如果您要等待用户注意到的时间,您可能希望显示progress bar(或splash screen,也许)。

    MyApp::MyApp(QWidget *parent) : ....
    {
        doSomeInits();
        {
            QSpashScreen splash;
            splash.connect(configManager, SIGNAL(updateCompleted()), SLOT(close()));
            configManager->updateConfigurations(); 
            splash.exec();
        }
        doReaminingInitsThatHadToWaitForConfigMgr();
    }
    

    【讨论】:

      【解决方案3】:

      另一种解决方案是使用QSignalSpy::wait()。此功能是在 Qt 5 中引入的,并且完全符合您的要求。

      我看到该类的唯一问题是它是在 QtTest 模块中提供的。就我而言,我发现它在测试代码时非常有用,但它可能不是生产代码的最佳解决方案。

      【讨论】:

      • 内部好像和chalup的回答一样。
      猜你喜欢
      • 2022-01-04
      • 2016-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多