2

QtConcurrent を使用して重い背景画像処理を行っていますが、画像の一部が徐々に更新されている間に画像を表示したいと考えています。画像の各行は個別に計算され、ファンクタに渡されます。

完全な画像を計算するには、QtConcurrent に渡すアイテムのシーケンスをマップし、計算が完了すると各行が信号を発します。

クラス Worker のインスタンス化は次のとおりです。

    //living in the main(gui) thread !
    Worker::Worker(VideoEngine* engine):_engine(engine){
        _watcher = new QFutureWatcher<bool>;
        _watcher->setPendingResultsLimit(200);
        connect(_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(onProgressUpdate(int)));
        connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));
    }

進行状況を報告するスロットは次のとおりです。

void Worker::onProgressUpdate(int i){
    if(i < (int)_rows.size() && i%10==0){
         cout << " index = " << i << " y = "<< _rows[i] << endl;
        _engine->checkAndDisplayProgress(_rows[i],i);
    }
}

今の使い方:

void Worker::_computeTreeForFrame(.../*unrelevant args*/){
....
....
    _watcher->setFuture(
                   QtConcurrent::mapped(_sequence,
                   boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
    }
}

すべてのシグナルが発行されますが、スロット onProgressUpdate は Qtconcurrent::mapped がシーケンス内のすべてのアイテムで完了した場合にのみ呼び出されます。

実行すると、シーケンスの処理中に大きな遅延が発生し、その後すべてのスロットが順番に実行されます。

すべてのタイプのシグナル/スロット接続を試しましたが、どれもこの動作を変更しませんでした。

どんな手掛かり ?

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++ Shf 提案後の編集 +++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++

呼び出しは今までメイン (gui) スレッドで行われていました。呼び出しを次のように変更しました:

_computeFrameWatcher->setFuture(QtConcurrent::run(_worker,&Worker::computeTreeForFrame));

別のスレッドで実行されるようになったため_computeTreeForFrame、QtConcurrent::mapped への呼び出しを次のように変更しました。

_watcher->setFuture(QtConcurrent::mapped(_sequence,
                     boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();

これにより、以前とまったく同じ動作が発生します。

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++ マレク R 提案後の編集 ++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++

わかりましたので、テストを行いました。ここに私が観察したものがあります:

QtConcurrent::map :

  • 信号を出さないresultReadyAt(int)

QtConcurrent::mapped

  • 終了resultReadyAt(int)時のみ出力

map 関数の呼び出しが別のスレッドで行われても、同じ動作が発生します。

progressValueChanged(int)Qt progressDialog の例が示すように、シグナルも試しました。信号は、画像の 2 行 (最初と最後)progressValueChanged(int) に対してのみ発せられます。これは、Qt の進行状況ダイアログの例ではスムーズに出力されるため、非常に奇妙です。

メイン スレッドとは別のスレッドでマップ関数を起動するように Qt の例を少し変更しましたが、その場合でも問題なく動作します。

問題は別の場所から発生する必要があります。

GUI イベント ループが予期しないことを行っている可能性があります。私には何の手がかりもありません。

QtConcurrent::mappedReduced を試して、結果を報告します:-)

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++ QtConcurrent::mappedReduced を試した後の編集 +++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++

「マップ」機能が完了した場合にのみ機能せず、「縮小」機能を呼び出します。つまり、以前のシグナル/スロットメカニズムと同じことを行います。

私は今、可能性が少なくなりつつあります

++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++ 編集 Qt の進行状況ダイアログに近いソリューションに戻りました例 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++

Qt の例と同じ動作が得られない場合は、何かが間違っているに違いありません。

コードは次のとおりです。

//created in the main thread! (gui)
Worker::Worker(VideoEngine* engine):_engine(engine),_watcher(0){
    _watcher = new QFutureWatcher<void>;
    _watcher->setPendingResultsLimit(200);
    connect(_watcher,SIGNAL(progressValueChanged(int)), _engine, 
                    SLOT(onProgressUpdate(int)));
    connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));

}

//executed on the main thread
void Worker::computeTreeForFrame(...){
...
_watcher->setFuture(QtConcurrent::map(_sequence,boost::bind(metaEnginePerRow,_1,output)));
...
}

computeTreeForFrame の呼び出し...

...
    _worker->computeTreeForFrame();
...

この呼び出しはslot で行われます。

前に述べたように、行 0 と最後の行のシグナルを送信しますが、他には何も送信しません。

これは Qt の例とまったく同じではないでしょうか?

4

2 に答える 2

0

ドキュメントから判断すると、QtConcurrent::mappedは VideoEngine::metaEnginePerRow を別のスレッドに置かないようです。画像が GUI と同じスレッドで処理される場合、説明したように、選択した接続の種類に関係なく、処理後にスロットが実際に実行されます。

解決策はWorker::_computeTreeForFrame、別のスレッドで(私が理解しているように、メイン処理機能を)実行するか、おそらく を介して別のスレッドにオブジェクトQtConcurrent::runを配置することです。次に、使用する接続タイプは次のとおりです(または、接続の前に別のスレッドを配置する場合は、 Qt::AutoConnection でも接続できます。呼び出し元と受信者は別のスレッドになるため、qt は自動的に QueuedConnection` を選択します)。WorkerQObject::moveToThread()Qt::QueuedConnectionWorkeror Qt::UniqueConnection

編集:

よくわかりませんが、あなた_watcher = new QFutureWatcher<bool>;はまだメインスレッドで作成されており、呼び出した場合

_watcher->setFuture(QtConcurrent::mapped(_sequence,
                 boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();

GUI スレッドを待機するように_watcher設定します。これは、作成されたスレッドまたはこのコマンドが実行されるスレッドで行われます。関数_watcher->setFuture(QtConcurrent::mapped(_sequence, boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));の終わりが_watcher->waitForFinished();必要な場合は?Qtは実行直後にスレッドを破棄し、処理関数を実行するように設定しましたが、なぜ待つのですか?

そして_computeFrameWatcher、タイプでなければなりませんQFuture<void*>

EDIT2:

わかりました、あきらめる前に、テストすることをお勧めしますQObject::moveToThread

を呼び出す前に_worker->computeTreeForFrame();、別のスレッドに入れてください。

QThread *workerThread=new QThread();
_worker->moveToThread();
_worker->computeTreeForFrame();
/* connect _worker's finished signal with workerThread::quit and deleteLater slots */

_worker 内のすべての接続は DirectConnection である必要があり、_worker とメイン (GUI) スレッド間のすべての接続は QueuedConnection で接続する必要があります。また、_worker コンストラクターで新しいスレッドを作成し、それをすぐに別のスレッドに移動することもおそらく良い方法です。これにより、_worker のデストラクタでスレッドを破棄でき、GUI スレッドでのスレッドの問題について心配する必要がなくなります。

于 2013-08-30T13:52:20.743 に答える
0

タスクの説明から、mappedReducedを使用する必要があるようです。問題は、部分的な結果を得る良い方法がわからないことです。この問題を解決する 1 つの方法は、reduce 関数からシグナルを発行することです。

このスレッドが役立つ可能性があります。

于 2013-08-30T12:36:19.857 に答える