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 在单独的线程上连接信号/插槽