QtConcurrent::run 发出信号

Posted

技术标签:

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

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

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

谢谢

【问题讨论】:

【参考方案1】:

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

// https://github.com/KubaO/***n/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() 中使用 QFuture 和 QFutureWatcher。

【讨论】:

【参考方案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;

【讨论】:

以上是关于QtConcurrent::run 发出信号的主要内容,如果未能解决你的问题,请参考以下文章

使用 QtConcurrent::run 在单独的线程上连接信号/插槽

从其他线程发出信号

QtConcurrent::run 如何停止后台任务

使用 QtConcurrent::run() 修改成员变量?

停止由 QtConcurrent::run 启动的线程?

qtchartsetreverse卡顿