【问题标题】:QEventLoop for synchronous wait for signalQEventLoop 用于同步等待信号
【发布时间】:2014-02-20 09:28:12
【问题描述】:

我正在使用 Qt5,QCoreApplication。 为了使代码可读且易于维护,我需要在类/线程 A 中编写一个阻塞方法,该方法将发出信号,连接到不同线程 B 中的插槽,然后等待答案或超时在线程 B 中异步发生。 我首先考虑的是一个自然的解决方案:让线程 B 回复一个连接到线程 A 中插槽的信号,然后以某种方式等待它。似乎 QEventLoop 可以为我做到这一点。但是我一直在阅读相互矛盾的陈述:这就是模式,但如果可以的话,请避免它:-)。 我很确定我可以通过在 B 准备好时释放的 0 QSemaphore 上阻止 A 来实现我的目的。这样,代码可能不会复杂得多。 有经验的 Qt 开发人员怎么看? 有没有好的解决方案,或者您是否在我的描述中发现了一些分析缺陷的症状(即您认为我永远不需要做这样的事情吗?:-))?

【问题讨论】:

    标签: c++ multithreading qt


    【解决方案1】:

    您可以利用的关键要素是Qt::BlockingQueuedConnection

    此连接类型允许您从插槽传递返回值。您可以在信号槽连接中使用它。也可以通过QMetaMethod::invoke/QMetaObject::invokeMethod机制,不使用信号直接调用槽。

    #include <QDebug>
    #include <QThread>
    #include <QCoreApplication>
    
    class Master : public QObject {
      Q_OBJECT
    public:
      Q_SIGNAL bool mySignal();
      Q_SLOT void process() {
        if (mySignal()) { // this can be a blocking call
          qDebug() << "success!";
        }
        thread()->quit();
      }
    };
    
    class Slave : public QObject {
      Q_OBJECT
    public:
      Q_SLOT bool mySlot() {
        // do whatever processing is needed here
        // It's OK to call QCoreApplication::processEvents here
        return true;
      }
    };
    
    int main(int argc, char** argv) {
      QCoreApplication app(argc, argv);
      QThread masterThread, slaveThread;
      Master master;
      Slave slave;
      master.moveToThread(&masterThread);
      slave.moveToThread(&slaveThread);
      slave.connect(&master, SIGNAL(mySignal()), SLOT(mySlot()),
                    Qt::BlockingQueuedConnection);
      masterThread.start();
      slaveThread.start();
      QMetaObject::invokeMethod(&master, "process");
      masterThread.wait();
      slaveThread.quit();
      slaveThread.wait();
      return 0;
    }
    
    #include "main.moc"
    

    【讨论】:

    • 非常感谢!我不知道 Qt::BlockingQueuedConnection 返回值。我似乎无法在官方 Qt 文档中找到它。我错过了吗?然而它并没有解决我的问题,因为我的线程 B 不能同步产生答案。睡了一夜之后,我想我会在线程 B(我的主线程)中使用状态机。线程是为那些不能编程状态机的人准备的,对吧?不过,如果我有更多的声誉,我会投票给你的答案。干杯。
    • @nilo 阻塞队列连接肯定被记录在案。只需查找connect 的参数。
    • @nilo 我不明白你的意思是从属线程不能同步产生答案。一个线程必须处于控制之中,而另一个线程是从属的。从属线程上的调用是完全同步的——它仅在控制点位于该线程的事件循环中时发生(因此线程不忙于做任何其他事情),并且只有在主线程被阻塞并等待结果时才会发生。跨度>
    • @nilo 如果您希望线程 A 与线程 B 异步,那么就不要使用阻塞连接。线程 A 向线程 B 发送信号,稍后线程 B 向线程 A 发送信号,此时线程 A 决定下一步该做什么。这正是状态机的工作方式,根本不涉及阻塞。一个设计合理的状态机是完全异步的并且只执行短的操作。 QStateMachineFramework 是你的朋友。
    • @ober 关于 BlockingQueuedConnection,文档说:“与 QueuedConnection 相同,除了当前线程阻塞直到插槽返回。这种连接类型应该只用于发射器和接收器在不同线程中的情况。”。没有说明返回值。同样,可能还有其他一些文档对此进行了解释,但我没有找到任何内容。
    【解决方案2】:

    如果你只想在你的线程中发出一个信号,这意味着你的主线程将有一个插槽来连接你的线程信号,这很简单,只需发出它。 但是如果你想在你的线程中有一个槽,并在你的线程中接收信号等等,你必须在你的 run 方法中使用 QEventloop。

    通常,我会使用 QThread::wait 来等待其他线程结束。

    这里要小心,一些 Qt 对象不能像 QSql* 和 QTcpSocket 那样跨线程工作....

    【讨论】:

      猜你喜欢
      • 2021-04-10
      • 2020-03-29
      • 1970-01-01
      • 1970-01-01
      • 2011-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多