2

アプリケーションに 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.cppQt 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);
4

1 に答える 1

5

どうやらこれは設計によるものです。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を使用して実装された を返しますQFutureInterface。には、 と の両方にQFutureInterfaceそのコードが含まれています。waitForFinished()waitForResult()

stealRunnableの文書化されていないプライベート メソッドですQThreadPool。したがって、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()ものからキューから取り出されていない場合、 orを呼び出すと、現在のスレッドで実行されます (つまり、同時に実行されません)。QThreadPoolwaitForFinishedwaitForResult

つまり、次のようなコード (および私が質問で行ったこと) が不可解な方法で失敗する可能性があります。

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.
}

と を使用して、デザイン (get から未来をQNetowrkAccessManager取得) を機能さstd::futureせましたstd::async

于 2015-02-05T03:00:19.103 に答える