QThread:使用 GUI 反馈阻止进行并行执行
QThread: Making parallel execution with GUI-Feedback blocking?
>场景
比方说,我有一个名为 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. 为每个工作人员创建一个线程
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 );
}
当用户单击 UI 按钮时运行load()
。
问题
如果我想在所有工作线程完成之前进行parallelRun
块,而不冻结QProgressDialog
,我应该如何扩展此代码?
审议
使用屏障
我尝试在parallelRun
例程的末尾添加以下代码:
QApplication::processEvents();
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}
这几行额外代码的影响是,LoadUI::progress
永远不会被输入,因为GUI线程处于睡眠状态,因此它的事件循环不会被处理:在Qt中,信号通过将信号发布到线程的事件循环来传递到插槽,与插槽所属的对象相关联。这就是为什么工人的progressed
信号永远不会发出的原因。
我认为,适当的解决方案是在工作人员发出progressed
信号时在 GUI 线程中运行QApplication::processEvents()
。另一方面,我想这无法完成,因为 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() );
我不认为这是一个不错的解决方案,但至少它有效。如何在没有主动等待的缺点的情况下做到这一点?
提前感谢!
线程的finished()
信号来等待它们在主 GUI 循环中全部完成,而不是使用 QApplication::processEvents
。进度对话框模式将确保只有该对话框窗口处于活动状态,直到显式关闭。
class WorkerManager : public QObject {
Q_OBJECT
private:
// to be able to access the threads and ui, they are defined as a members
std::vector<std::unique_ptr<QThread> > threads;
LoadUI *ui;
int finishedThreadCount;
public:
WorkerManager()
: finishedThreadCount(0)
{
// Open the QProgressDialog
...
// Create and start the threads
...
// Connect the finished() signal of each thread
// to the slot onThreadFinished
for( auto it = threads.begin(); it != threads.end(); ++it ) {
QObject::connect(
it->get(), SIGNAL(finished()),
this, SLOT(onThreadFinished()) );
}
}
private slots:
void onThreadFinished() {
++finishedThreadCount;
if(finishedThreadCount == threads.size())
{
// clean up the threads if necessary
// close the dialog
// and eventually destroy the object this itself
}
}
};
或者,您可以运行嵌套QEventLoop
以等待线程同步完成,同时仍保持 GUI 响应:
// Open the QProgressDialog
...
// Create and start the threads
...
// Create and run a local event loop,
// which will be interrupted each time a thread finishes
QEventLoop loop;
for( auto it = threads.begin(); it != threads.end(); ++it )
{
QObject::connect(
it->get(), SIGNAL(finished()),
&loop, SLOT(quit()) );
}
for(int i = 0, threadCount = threads.size(); i < threadCount; ++i)
loop.exec();
如果进度仅在工作完全完成时达到最大值,则可以使用progressDlg->exec()
而不是QEventLoop
,该将阻止直到达到最大值或直到用户单击"取消"按钮。
而不是构建自己的。也许QThreadPool就是你要找的?
QThreadPool具有等待所有工人的功能。
- C++17中的并行执行策略
- 在 Radeon 卡上并行执行多个 OpenCL 内核
- 使用 OpenMP 并行执行比串行执行 c++ 花费更长的时间,我计算执行时间是否正确?
- 如何将GDB与OpenMP和并行编程使用
- 用于 std::generate_n 的并行执行可变 lambda 生成器
- 卷曲轻松执行使用多个线程C++编程
- 在 C++Amp 中并行执行的几种算术运算
- 为什么并行执行Tcl_ExprDouble的独立Tcl解释器需要互斥
- QThread:使用 GUI 反馈阻止进行并行执行
- CUDA - 了解线程的并行执行(扭曲)和合并的内存访问
- 下面的代码是否运行两个子进程,从 C++ 中的单个父进程并行执行
- 提升io_service非阻塞并行执行
- 并行执行的 OpenCL 验证
- 并行执行不更新我的变量
- CUDA动态并行性:使用纹理内存时无效的全局写入
- 使用pthread_cond_broadcast并行执行
- boost::asio库的async_*函数是否由操作系统并行执行?
- 非常长的查询执行使用c++, Sql Server和ODBC连接
- 在c++中,包含后增量的表达式能否与该表达式的其他部分并行执行?
- 并行执行比顺序执行快的示例