Note that there are some explanatory texts on larger screens.

plurals
  1. POQThread: Making parallel execution with GUI-Feedback blocking?
    text
    copied!<h1>Scenario</h1> <p>Lets say, I have a procedure called <code>parallelRun</code>. It would take a list of workers, each having a <code>getWorkAmount():int</code>, a <code>run()</code> method, a <code>finished()</code> signal and a <code>cancel()</code> slot:</p> <pre><code>void parallelRun( std::vector&lt; Worker* &gt; workers ); </code></pre> <p>Its implementation should:</p> <h2>1. Open a <code>QPogressDialog</code>:</h2> <pre><code>unsigned int totalWorkAmount = 0; for( auto it = workers.begin(); it != workers.end(); ++it ) { totalWorkAmount += ( **it ).getWorkAmount(); } LoadUI ui( 0, totalWorkAmount, this ); </code></pre> <p>with</p> <pre><code>class LoadUI : public QObject { Q_OBJECT public: LoadUI( int min, int max, QWidget* modalParent ) : totalProgres( 0 ) , progressDlg( "Working", "Abort", min, max, modalParent ) { connect( &amp;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; } </code></pre> <h2>2. Create one thread for each worker</h2> <pre><code>std::vector&lt; std::unique_ptr&lt; QThread &gt; &gt; threads; for( auto it = workers.begin(); it != workers.end(); ++it ) { std::unique_ptr&lt; QThread &gt; thread( new QThread() ); Worker* const worker = *it; worker-&gt;moveToThread( thread.get() ); QObject::connect( worker, SIGNAL( finished() ), thread.get(), SLOT( quit() ) ); QObject::connect( &amp;ui, SIGNAL( canceled() ), worker, SLOT( cancel() ) ); QObject::connect( *it, SIGNAL( progressed( int ) ), &amp;ui, SLOT( progress( int ) ) ); thread-&gt;start( priority ); threads.push_back( std::move( thread ) ); } </code></pre> <h2>3. Run them simultaneously</h2> <pre><code>for( auto it = workers.begin(); it != workers.end(); ++it ) { QMetaObject::invokeMethod( *it, "run", Qt::QueuedConnection ); } </code></pre> <p><code>load()</code> is run when the user clicks an UI-button.</p> <h1>Problem</h1> <p>How am I supposed to extend this code, if I want to make <code>parallelRun</code> block until all workers are finished, without freezing the <code>QProgressDialog</code>?</p> <h2>Deliberations</h2> <h3>Using a barrier</h3> <p>I tried adding the following code at the end of the <code>parallelRun</code> routine:</p> <pre><code>QApplication::processEvents(); for( auto it = threads.begin(); it != threads.end(); ++it ) { ( **it ).wait(); } </code></pre> <p>The impact of this few lines of extra-code is, that <code>LoadUI::progress</code> is <em>never entered</em>, since the GUI-thread is <em>asleep</em> and therefore it's event loop isn't processed: In Qt, signals are delivered to slots by posting them to the event loop of the thread, associated to the object the slot belongs to. This is why the <code>progressed</code> signal of a worker is never delivered.</p> <p>I think, the appropriate solution would be to run <code>QApplication::processEvents()</code> <em>within the GUI-thread</em> anytime a <code>progressed</code> signal is emitted by a worker. On the other hand, I guess this cannot be done, since the GUI-thread is asleep.</p> <h3>Another possible solution</h3> <p>Another possibility would be to use an <em>active waiting</em>-like solution:</p> <pre><code>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(); } </code></pre> <p>This also requires adding the following line of code right after <code>thread-&gt;start( priority );</code>:</p> <pre><code>while( !thread-&gt;isRunning() ); </code></pre> <p>I don't think that this is a nice solution, but at least it works. How can this be done without the drawbacks of active waiting?</p> <p>Thanks in advance!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload