gpt4 book ai didi

c++ - QThread:使用 GUI 反馈阻塞进行并行执行?

转载 作者:太空狗 更新时间:2023-10-29 21:48:12 27 4
gpt4 key购买 nike

场景

比方说,我有一个名为 parallelRun 的过程。它需要一个 worker 列表,每个 worker 都有一个 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 阻塞直到所有 worker 完成,而不卡住 QProgressDialog,我应该如何扩展这段代码?

审议

使用屏障

我尝试在 parallelRun 例程的末尾添加以下代码:

QApplication::processEvents();
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}

这几行额外代码的影响是,LoadUI::progress 从未进入,因为 GUI 线程处于休眠状态 因此它的事件循环不被处理:在 Qt 中,信号通过将它们发布到线程的事件循环来传递到槽,与槽所属的对象相关联。这就是为什么永远不会传送 worker 的 progressed 信号。

我认为,适当的解决方案是在 progressed 信号出现时随时在 GUI 线程中运行 QApplication::processEvents() 一名 worker 发出的。另一方面,我想这不可能完成,因为 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 它将阻塞直到最大值到达或直到用户点击“取消”按钮。

关于c++ - QThread:使用 GUI 反馈阻塞进行并行执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11360842/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com