QtConcurrent映射和进度报告

时间:2013-08-30 07:38:44

标签: c++ multithreading qt signals-slots qtconcurrent

我正在使用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示例的作用吗?

2 个答案:

答案 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线程中的线程问题