我正在使用QtConcurrent进行一些繁重的背景图像处理,我希望显示图像,同时逐步更新部分图像。 图像的每一行都是单独计算的,并传递给一个仿函数。
为了计算完整的图像,我有一个项目序列,我传递给QtConcurrent映射,每一行在完成计算时发出信号
这是类Worker的实例化:
//living in the main(gui) thread !
Worker::Worker(VideoEngine* engine):_engine(engine){
_watcher = new QFutureWatcher<bool>;
_watcher->setPendingResultsLimit(200);
connect(_watcher, SIGNAL(resultReadyAt(int)), this, SLOT(onProgressUpdate(int)));
connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));
}
以下是报告进度的插槽:
void Worker::onProgressUpdate(int i){
if(i < (int)_rows.size() && i%10==0){
cout << " index = " << i << " y = "<< _rows[i] << endl;
_engine->checkAndDisplayProgress(_rows[i],i);
}
}
现在用法:
void Worker::_computeTreeForFrame(.../*unrelevant args*/){
....
....
_watcher->setFuture(
QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
}
}
发出所有信号,但只有当Qtconcurrent :: mapped完成序列中的所有项目时才会调用onProgressUpdate插槽。
当序列处理时执行它时会有很大的延迟,然后所有的插槽都会顺序执行。
我尝试过所有类型的信号/插槽连接,但没有一个改变这种行为。
有任何线索吗?
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++ 在Shf建议之后编辑 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
直到现在在主(gui)线程中进行了调用。 我把电话改为:
_computeFrameWatcher->setFuture(QtConcurrent::run(_worker,&Worker::computeTreeForFrame));
由于_computeTreeForFrame
现在在另一个线程中执行,我将对QtConcurrent :: mapped的调用更改为:
_watcher->setFuture(QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();
这导致与以前完全相同的行为。
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++ 在Marek R建议之后编辑 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
好的,所以我做了这样的测试,这就是我观察到的:
QtConcurrent :: map:
resultReadyAt(int)
QtConcurrent ::映射
resultReadyAt(int)
如果对map函数的调用是在单独的线程中完成的,则遇到相同的行为无关紧要。
我还尝试了Qt progressDialog示例所示的信号progressValueChanged(int)
。
信号progressValueChanged(int)
仅在图像(第一行和最后一行)中发出2行。
这非常奇怪,因为在Qt进度对话框示例中它可以顺利发出。
我改变了一点Qt示例,在主线程之外的另一个线程中启动map函数,在这种情况下它仍能正常工作。
问题必须来自其他地方。
也许GUI事件循环正在做一些我不期望的事情?我不知道是什么。
我现在将尝试QtConcurrent :: mappedReduced并报告结果: - )
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++ 尝试QtConcurrent :: mappedReduced后编辑 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
它不起作用,仅在“map”功能完成时调用“reduce”功能。换句话说,它与先前的信号/时隙机制相同。
我现在的可能性很低
+++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++ 编辑我回到了与Qt进度对话框示例一样接近的解决方案 ++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++
如果我无法获得与Qt示例相同的行为,那么肯定会出错。
现在是代码:
//created in the main thread! (gui)
Worker::Worker(VideoEngine* engine):_engine(engine),_watcher(0){
_watcher = new QFutureWatcher<void>;
_watcher->setPendingResultsLimit(200);
connect(_watcher,SIGNAL(progressValueChanged(int)), _engine,
SLOT(onProgressUpdate(int)));
connect(_watcher, SIGNAL(finished()), engine, SLOT(engineLoop()));
}
//executed on the main thread
void Worker::computeTreeForFrame(...){
...
_watcher->setFuture(QtConcurrent::map(_sequence,boost::bind(metaEnginePerRow,_1,output)));
...
}
对computeTreeForFrame的调用......
...
_worker->computeTreeForFrame();
...
此调用在广告位中完成。
它会发出第0行和最后一行的信号,如前所述,但不会发出任何其他信号。
这不应该完全是Qt示例的作用吗?
答案 0 :(得分:0)
从任务说明看起来您应该使用mappedReduced。问题是我没有看到获得部分结果的好方法。克服这个问题的一种方法是发出信号形式的降低功能。
this thread可能会有所帮助。
答案 1 :(得分:0)
看来,QtConcurrent::mapped没有将VideoEngine :: metaEnginePerRow放在另一个线程中,从文档判断。如果在与GUI相同的线程中处理图像,那么无论您选择何种类型的连接,您的插槽确实将在处理后执行,就像您所描述的那样。
解决方案是通过Worker::_computeTreeForFrame
在另一个线程中运行QtConcurrent::run
(我理解为您的主要处理函数),或者将Worker
对象放在另一个线程中,可能是通过{{ 1}}。然后,您应该使用的连接类型是QObject::moveToThread()
(或者如果您在连接之前将Qt::QueuedConnection
放在另一个线程中,您甚至可以连接Qt :: AutoConnection Worker
,来电者和接收者将在不同的线程中,所以qt会自动选择QueuedConnection`)
编辑:
我不确定,但你的or Qt::UniqueConnection
仍在主线程中创建,如果你打电话
_watcher = new QFutureWatcher<bool>;
将_watcher->setFuture(QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();
设置GUI线程等待,在它创建的内容或线程中,执行此命令的位置。如果_watcher
如果函数结束,则需要_watcher->setFuture(QtConcurrent::mapped(_sequence,
boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
吗? Qt会在执行后立即销毁线程并将你的处理函数设置为运行,为什么要等待?
_watcher->waitForFinished();
应为_computeFrameWatcher
类型。
EDIT2:
好的,在我放弃之前,我建议你测试QFuture<void*>
:
在致电QObject::moveToThread
之前,请将其放入另一个主题:
_worker->computeTreeForFrame();
并且_worker中的所有连接都应该是DirectConnection,_worker和main(GUI)线程之间的所有连接都应该与QueuedConnection连接。也可以在_worker构造函数中创建新线程并立即将其移动到另一个线程,这样就可以在_worker的析构函数中销毁线程并且不用担心GUI线程中的线程问题