2

我正在使用 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建议后编辑+++++++++++ ++++++++++++++++++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++

到目前为止,该调用是在 main(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::mapped

  • resultReadyAt(int)仅在完成时发出

对 map 函数的调用是否在单独的线程中完成并不重要,会遇到相同的行为。

progressValueChanged(int)正如 Qt progressDialog 示例所暗示的那样,我还尝试了该信号。信号progressValueChanged(int) 在图像中的 2 行(第一行和最后一行)中发出。这真的很奇怪,因为在 Qt 进度对话框示例中它是顺利发出的。

我对 Qt 示例进行了一些更改,以在主线程之外的另一个线程中启动 map 函数,在这种情况下它仍然可以正常工作。

问题必须来自其他地方。

也许 GUI 事件循环正在做一些我没想到的事情?我不知道是什么。

我现在将尝试 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();
...

这个调用是在一个 slot中完成的。

如前所述,它为第 0 行和最后一行发出信号,但不发出任何其他信号。

这不应该完全像 Qt 示例所做的那样吗?

4

2 回答 2

0

从文档来看,似乎QtConcurrent::mapped没有将 VideoEngine::metaEnginePerRow 放在另一个线程中。如果图像在与 GUI 相同的线程中处理,那么您的插槽确实会在处理后执行,无论您选择哪种类型的连接,正如您所描述的那样。

解决方案是通过在另一个线程中运行Worker::_computeTreeForFrame(据我了解,您的主要处理功能)QtConcurrent::run或将您的Worker对象放在另一个线程中可能通过QObject::moveToThread(). 然后,您应该使用的连接类型是Qt::QueuedConnection(或者如果您将Worker在连接之前放入另一个线程,即使使用 Qt::AutoConnection 也可以连接or Qt::UniqueConnection,调用者和接收者将在不同的线程中,因此 qt 将自动选择 QueuedConnection`)

编辑:

我不确定,但你_watcher = new QFutureWatcher<bool>;仍然在主线程中创建,如果你打电话

_watcher->setFuture(QtConcurrent::mapped(_sequence,
                 boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));
_watcher->waitForFinished();

_watcherGUI 线程设置为等待,在它被创建的地方或线程中,执行这个命令。如果_watcher->setFuture(QtConcurrent::mapped(_sequence, boost::bind(&VideoEngine::metaEnginePerRow,_1,output)));如果函数结束,是否_watcher->waitForFinished();需要?Qt 将在线程执行后立即销毁线程,并且您将处理函数设置为运行,为什么还要等待?

并且_computeFrameWatcher应该是QFuture<void*>类型。

编辑2:

好的,在我放弃之前,我建议你测试一下QObject::moveToThread

在你打电话之前_worker->computeTreeForFrame();,把它放在另一个线程中:

QThread *workerThread=new QThread();
_worker->moveToThread();
_worker->computeTreeForFrame();
/* connect _worker's finished signal with workerThread::quit and deleteLater slots */

_worker 中的所有连接都应该是 DirectConnection,并且 _worker 和主(GUI)线程之间的所有连接都应该使用 QueuedConnection 连接。此外,在 _worker 构造函数中创建新线程并立即将其移动到另一个线程可能很好,这样您可以在 _worker 的析构函数中销毁线程,而不必担心 GUI 线程中的线程问题

于 2013-08-30T13:52:20.743 回答
0

从任务描述看来,您应该使用mappedReduced。问题是我看不到获得部分结果的好方法。克服这个问题的一种方法是从reduce函数发出信号。

这个线程可能会有所帮助。

于 2013-08-30T12:36:19.857 回答