アプリケーションに 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: 実行されるラムダは、単純に HTTP GET を実行し、JSON 応答を解析してSomeType
オブジェクトに生成します。経由でアクセスされる結果QFuture
。
=== アップデート 2
どうやらこれは設計によるものです。qfutureinterface.cpp
Qt 5.4.0 行 293-295 から:
// 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);