【问题标题】:QtConcurrent::run emit signalQtConcurrent::run 发出信号
【发布时间】:2011-08-18 21:34:24
【问题描述】:

我想在 Qt 中从我用 QtConcurrent::run 调用的函数发出信号

这可能吗?似乎我的插槽永远不会被调用。所有的信号、槽和函数都是同一个类对象的一部分。我尝试在主线程和从线程中建立连接。我真的不在乎信号和槽是否在同一个线程中,我只想让它发生。

谢谢

【问题讨论】:

    标签: c++ multithreading qt


    【解决方案1】:

    以下在 Qt 4.8.7 中运行良好。信号从工作线程发出,并在主线程中消耗。我们断言槽在主线程中运行,函子在工作线程中运行。

    // https://github.com/KubaO/stackoverflown/tree/master/questions/concurrent-emit-qt4-7114421
    #include <QtCore>
    
    class Helper : public QObject {
       Q_OBJECT
    public:
       int n = 0;
       Q_SLOT void increment() {
          Q_ASSERT(QThread::currentThread() == qApp->thread());
          n++;
       }
    };
    
    int main(int argc, char **argv)
    {
       QCoreApplication app(argc, argv);
       Helper helper;
       Q_ASSERT(helper.n == 0);
       QtConcurrent::run([&]{
          Q_ASSERT(QThread::currentThread() != qApp->thread());
          QObject src;
          QObject::connect(&src, SIGNAL(destroyed(QObject*)), &helper, SLOT(increment()));
          QObject::connect(&src, SIGNAL(destroyed(QObject*)), &app, SLOT(quit()));
       });
       app.exec();
       Q_ASSERT(helper.n == 1);
    }
    
    #include "main.moc"
    

    在 Qt 5 中,您不需要帮助类来证明它可以工作:

    #include <QtConcurrent>
    
    int main(int argc, char **argv)
    {
       QCoreApplication app(argc, argv);
       int n = 0;
       Q_ASSERT(n == 0);
       QtConcurrent::run([&]{
          Q_ASSERT(QThread::currentThread() != qApp->thread());
          QObject src;
          QObject::connect(&src, &QObject::destroyed, &app, [&]{
             Q_ASSERT(QThread::currentThread() == qApp->thread());
             n ++;
             qApp->quit();
          });
       });
       app.exec();
       Q_ASSERT(n == 1);
    }
    

    【讨论】:

    • Qt 对 QFuture 有奇怪的语义,允许它在协程仍在运行时被销毁,这会导致奇怪的行为。请注意,它的 ISO 模拟的析构函数会等待。严格来说应该创建 QFuture 对象,调用 app.exec,然后等待 QFuture 完成(如果还没有完成)。
    【解决方案2】:

    您可以为该连接使用Qt::QueuedConnection(将其传递给建立连接的connect 调用),因为信号总是从与接收对象线程不同的线程发出。

    Qt::AutoConnection 也会做同样的事情,并将信号添加到接收对象线程的事件队列中。

    如果接收线程被阻塞,因此永远不会重新进入事件队列,则接收对象的槽无法接收到信号。

    【讨论】:

    • 这是不正确的:默认的自动连接就可以了。
    • @KubaOber:你错了。 OP 说所有插槽和信号都属于 ONE 对象。因此,AutoConnection 将评估为 DirectConnection。这可能不是 OP 想要的,因为这要求插槽以线程安全的方式使用对象。在这种情况下,必须手动完成此同步。 QueuedConnection 将消除手动同步的需要,因为插槽将在对象所在的线程中调用。 Qt 将处理实现该目标所需的同步。
    • 发件人的对象无关紧要。发送和接收线程之间的比较在信号发出时完成,并且在 current 线程 (QThread::currentThread) 和 target 线程 (@987654325) 之间完成@)。 发送对象的线程在该决定中没有任何意义。
    • @KubaOber:确实你是对的。我一直认为它考虑了发送者对象的线程亲和性并静态配置连接。但它确实会动态检查线程并且总是*做正确的事情。将连接静态配置为 Queued 可能仍然是有益的,因为这消除了 Qt 比较当前/接收者线程的需要。该优化显然仅适用于始终从不同线程触发信号的情况。顺便说一句,这正是 OP 的情况。
    【解决方案3】:

    你真的应该在 QtConcurrent::run() 中使用 QFutureQFutureWatcher

    【讨论】:

      【解决方案4】:

      是的,这是可能的。 看看小例子:

      在这个例子中,我们要实现多线程。 longProcess 函数进入线程池并在线程池中处理,然后长进程函数的答案返回主线程。

      Test.h

      Class Test: public QObject
      {
          Q_OBJECT
      public:
          explicit Test(QObject *parent = nullptr);
      
          void resultAvailable();
      
          static void doLongProcess(Test *test);
      
      signals:
          void finishedProcess(const QString &massage);
      
      public slots:
          void captureSignal(const QString &message);
      };
      

      Test.cpp

      void Test::resultAvailable()
      {
              QtConcurrent::run(&ContactsModelManager::doLongProcess, this);
      
              connect(this , &Test::finishedProcess,
                      this , &Test::captureSignal);
      }
      
      //attention!! doLongProcess is static fnuction
      void Test::doLongProcess(Test *test)
      {
          //this process is very long
          test->longProcess();
      }
       void Test::longProcess()
      {
          //do your process
      
          //at the end emit your signal 
          emit finishedProcess("finished process in another thread");
      }
      
      void Test::captureSignal(const QString &message)
      {
         Qdebug() << "message is: " << message;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-01-12
        • 1970-01-01
        • 2018-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多