设想
可以说,我有一个名为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.为每个worker创建一个线程
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();
}
这几行额外代码的影响是,LoadUI::progress
永远不会进入,因为 GUI 线程处于休眠状态,因此它的事件循环没有被处理:在 Qt 中,信号通过将它们发布到事件循环来传递到插槽线程,与插槽所属的对象相关联。这就是为什么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() );
我不认为这是一个很好的解决方案,但至少它有效。如果没有主动等待的缺点,如何做到这一点?
提前致谢!