저는 응용 프로그램에 QFuture 기반 비동기 네트워킹 외관을 구축했습니다. 대략은 다음과 같이 작동QtConcurrent ::는 메인 스레드에서 어떻게 실행됩니까?
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()
주를 차단하는 것이라고했다 배경 스레드가 내 네트워크 요청을 실행하는 동안 그래서
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
: 나는 (아래에서 위로 읽음)를 참조 차단되는 대신 나는 내 waitForFinished()
주 스레드에서 참조 스택 추적에서
ASSERT: "m_blockedRunLoopTimer == m_runLoopTimer" in file eventdispatchers/qeventdispatcher_cf.mm, line 237
하지만, 대신 나는 qFatal
오류 QFuture
값을 얻으려고 기다리는 대신 내 생각에 동시 작업은 주 스레드에서 실행됩니다. 이로 인해 위에서 설명한 get()
함수가 호출되어 QEventLoop
의 이벤트를 수신합니다. 그 사이에, QTimer
는 다시 발화한다. 그리고 나는 위의 주장을 얻는다.
내가 뭔가 잘못하고 있는거야, 아니면 QtConcurrent::run
이 주 스레드로 다시 돌아갈 수 있다고 완벽하게 유효합니까?
=== 업데이트 1
@peppe : 람다 실행 중이 단순히 HTTP GET 수행하고 SomeType
객체로 JSON 응답을 파싱하여 생성한다. 결과는 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);
QtConcurrent를 사용하여 네트워크 요청을 디스패치하는 이유는 무엇입니까? 그리고 '달리는'람다 안에서 무슨 일이 일어나고 있는지 자세히 설명해 주시겠습니까? 그곳에 뭔가 잘못된 점이 있습니다. – peppe
여러 네트워크 요청을 발행하고 결과를 나중에 보거나, 미래를 사용하는 간단한 방법을 원합니다. 'QtConcurrent'는 그게 딱 맞는 것 같았습니다. 질문을 람다에 대한 세부 정보로 업데이트했습니다. – Tim
그러나 QNetworkAccessManager는 완전히 비동기입니다. 진행 상황을 알려주는 비동기 신호를 제공합니다. 요청을 발행하고 "나중에"검사 할 때의 문제점은 무엇입니까? – peppe