シナリオ
というプロシージャがあるとしましょうparallelRun
。getWorkAmount():int
それぞれが、run()
メソッド、finished()
シグナル、およびcancel()
スロットを持つワーカーのリストを取得します。
void parallelRun( std::vector< Worker* > workers );
その実装は次のことを行う必要があります。
1. を開きますQPogressDialog
:
unsigned int totalWorkAmount = 0;
for( auto it = workers.begin(); it != workers.end(); ++it )
{
totalWorkAmount += ( **it ).getWorkAmount();
}
LoadUI ui( 0, totalWorkAmount, this );
と
class LoadUI : public QObject
{
Q_OBJECT
public:
LoadUI( int min, int max, QWidget* modalParent )
: totalProgres( 0 )
, progressDlg( "Working", "Abort", min, max, modalParent )
{
connect( &progressDlg, SIGNAL( canceled() ), this, SLOT( cancel() ) );
progressDlg.setWindowModality( Qt::WindowModal );
progressDlg.show();
}
bool wasCanceled() const
{
return progressDlg.wasCanceled();
}
public slots:
void progress( int amount )
{
totalProgres += amount;
progressDlg.setValue( totalProgres );
progressDlg.update();
QApplication::processEvents();
}
signals:
void canceled();
private slots:
void cancel()
{
emit canceled();
}
private:
int totalProgres;
QProgressDialog progressDlg;
}
2. ワーカーごとに 1 つのスレッドを作成する
std::vector< std::unique_ptr< QThread > > threads;
for( auto it = workers.begin(); it != workers.end(); ++it )
{
std::unique_ptr< QThread > thread( new QThread() );
Worker* const worker = *it;
worker->moveToThread( thread.get() );
QObject::connect( worker, SIGNAL( finished() ), thread.get(), SLOT( quit() ) );
QObject::connect( &ui, SIGNAL( canceled() ), worker, SLOT( cancel() ) );
QObject::connect( *it, SIGNAL( progressed( int ) ), &ui, SLOT( progress( int ) ) );
thread->start( priority );
threads.push_back( std::move( thread ) );
}
3. 同時に実行する
for( auto it = workers.begin(); it != workers.end(); ++it )
{
QMetaObject::invokeMethod( *it, "run", Qt::QueuedConnection );
}
load()
ユーザーが UI ボタンをクリックすると実行されます。
問題
parallelRun
をフリーズせずに、すべてのワーカーが終了するまでブロックしたい場合、このコードをどのように拡張すればよいQProgressDialog
ですか?
審議
バリアの使用
parallelRun
ルーチンの最後に次のコードを追加してみました。
QApplication::processEvents();
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}
この数行の追加コードの影響は、GUI スレッドがスリープしているため、入力LoadUI::progress
されないため、イベント ループが処理されないことです。スロットが属するオブジェクトに関連付けられたスレッド。これが、ワーカーのシグナルが配信されない理由です。progressed
適切な解決策は、ワーカーによってシグナルが発行されるたびQApplication::processEvents()
に GUI スレッド内で実行することだと思います。progressed
一方、GUI スレッドがスリープしているため、これは実行できないと思います。
別の可能な解決策
別の可能性は、アクティブな待機のようなソリューションを使用することです。
for( auto it = threads.begin(); it != threads.end(); ++it )
{
while( ( **it ).isRunning() )
{
QApplication::processEvents();
}
}
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}
これには、 の直後に次のコード行を追加する必要もありますthread->start( priority );
。
while( !thread->isRunning() );
これは良い解決策だとは思いませんが、少なくとも機能します。アクティブな待機の欠点なしでこれを行うにはどうすればよいでしょうか?
前もって感謝します!