QtConcurrent :: run如何在主线程上运行?

时间:2015-02-04 21:53:19

标签: qt qtnetwork qtconcurrent qt5.4

我在我的应用程序中构建了一个基于QFuture的异步网络外观。大致它的工作原理如下:

namespace NetworkFacade {
    QByteArray syncGet(const QUrl& url) {
        QEventLoop l;
        QByteArray rc;

        get(url, [&](const QByteArray& ba) {
            rc = ba;
            l.quit();
        });

        l.exec();
        return rc;
    }

    void get(const QUrl& url, const std::function<void (const QByteArray&)>& handler) {
        QPointer<QNetworkAccessManager> m = new QNetworkAccessManager;

        QObject::connect(m, &QNetworkAccessManager::finished, [=, &m](QNetworkReply *r) {
            QByteArray ba;

            if (r && r -> error() == QNetworkReply::NoError)
                ba = r -> readAll();

            m.clear();

            if (handler)
                handler(ba);
        });
        m -> get(QNetworkRequest(url));
    }
}

我有一个QTimer触发主线程上的调用,执行以下操作(显然简化):

foreach(Request r, requests) {
    futures.push_back(get(r));
}

foreach(QFuture<SomeType> f, futures) {
    f.waitForFinished();
    [do stuff with f.result()]
}

我的假设是waitForFinished()会阻塞主线程,而后台线程会执行我的网络请求。相反,我收到qFatal错误:

ASSERT: "m_blockedRunLoopTimer == m_runLoopTimer" in file eventdispatchers/qeventdispatcher_cf.mm, line 237

在堆栈跟踪中,我在主线程上看到了waitForFinished(),但后来我没有看到(从下往上看):

com.myapp   0x0008b669 QEventDispatcherCoreFoundation::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 1753
com.myapp   0x000643d7 QIOSEventDispatcher::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 823
com.myapp   0x0130e3c7 QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 119
com.myapp   0x0130e5fb QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 539
com.myapp   0x0003a550 NetworkFacade::syncGet(QUrl const&) + 208
com.myapp   0x00037ed1 QtConcurrent::StoredFunctorCall0<std::__1::shared_ptr<QuoteFacade::Quote>, QuoteFacade::closingQuote(QString const&, QDate const&)::$_0>::runFunctor() + 49
com.myapp   0x00038967 QtConcurrent::RunFunctionTask<std::__1::shared_ptr<QuoteFacade::Quote> >::run() + 87
com.myapp   0x00038abc non-virtual thunk to QtConcurrent::RunFunctionTask<std::__1::shared_ptr<QuoteFacade::Quote> >::run() + 28
com.myapp   0x010dc40f QThreadPoolPrivate::stealRunnable(QRunnable*) + 431
com.myapp   0x010d0c35 QFutureInterfaceBase::waitForFinished() + 165

因此,我不是等待QFuture获取值,而是在主线程上发出我所谓的并发任务。这会导致调用上面概述的get()函数,该函数将侦听QEventLoop上的事件。与此同时,QTimer再次发射,我从上面得到断言。

我做错了什么,或者QtConcurrent::run可以导致控制权回到主线程是否完全有效?

===更新1

@peppe:正在执行的lambda只执行HTTP GET并生成将JSON响应解析为SomeType对象。结果通过QFuture

进行访问

===更新2

显然这是设计上的。来自Qt 5.4.0第293-295行的qfutureinterface.cpp

// To avoid deadlocks and reduce the number of threads used, try to
// run the runnable in the current thread.
d->pool()->d_func()->stealRunnable(d->runnable);

1 个答案:

答案 0 :(得分:2)

显然这是设计上的。来自Qt 5.4.0第293-295行的qfutureinterface.cpp:

// To avoid deadlocks and reduce the number of threads used, try to
// run the runnable in the current thread.
d->pool()->d_func()->stealRunnable(d->runnable);

QtConcurrent::run()返回使用QFuture实现的QFutureInterfaceQFutureInterface包含waitForFinished()waitForResult()中的代码。

stealRunnableQThreadPool的未记录的私有方法。因此在headerdoc中描述:

/*!
    \internal
    Searches for \a runnable in the queue, removes it from the queue and
    runs it if found. This function does not return until the runnable
    has completed.
*/

因此,如果由QRunnable在内部创建的QtConcurrent::run()已经从已分配给它的QThreadPool中出列,那么我们最终会遇到这种情况,然后调用{{ 1}}或waitForFinished将使它在当前线程上运行(即不同时运行。)

这意味着这样的代码(以及我在问题中所做的)可能会以神秘的方式失败:

waitForResult

我使用foreach (FuncPossiblyTriggeringQEvents fn, tasks) { futures.push_back(QtConcurrent::run(fn)); } foreach (QFuture<> f, futures) { f.waitForFinished(); // Some of my tasks will run in this thread, not concurrently. } QNetowrkAccessManager获得了我的设计(通过std::future获得了未来)。