我有一个Worker Thread,可以处理繁重而漫长的计算(长达十分之一秒)。这些计算产生数千个QLine
,代表动态增长的树的边缘。
这些边缘可以随时修改,因为它们通过检查成本(由距离表示)来连接树的节点。
我想要包含边缘的QGraphicsScene的平滑更新。
我尝试了信号和插槽:
工作线程会发出一个信号,因此当缓冲区已满时,该信号会被主线程捕获,从而可以处理该行的更新/绘制
这个信号仍然被主线程捕获,但是似乎它经常被发出,所以QGraphicsView
被QLine
阻塞了
更改缓冲区大小无关紧要
有替代方法吗?
主插槽是:
void MainWindow::update_scene(bufferType buffer)
{
for (int i = 0; i < buffer.size(); ++i)
{
if (buffer[i].first < (g_edges.size() - 1))
{
delete g_edges[buffer[i].first];
g_edges[buffer[i].first] = scene->addLine(buffer[i].second);
}
else
g_edges.push_back(scene->addLine(buffer[i].second));
}
}
请注意,
bufferType
的类型为QList<std::pair<int,QLine>>
。这是繁重的计算部分
while (T.size() < max_nodes_number && !_stop)
{
const cnode random_node = rand_conf ();
const cnode nearest_node = T.nearest_node (random_node);
cnode new_node = new_conf (nearest_node, random_node);
if (obstacle_free(nearest_node, new_node))
{
QList<cnode*> X_near = T.neighbours (new_node, max_neighbour_radius);
cnode lowest_cost_node = nearest_node;
qreal c_min = nearest_node.cost() + T.distance (nearest_node, new_node);
for (int j = 0; j < X_near.size(); ++j)
{
if (obstacle_free(*X_near[j], new_node) && ((X_near[j]->cost() + T.distance (*X_near[j], new_node)) < c_min))
{
c_min = X_near[j]->cost() + T.distance (*X_near[j], new_node);
lowest_cost_node = *X_near[j];
}
}
T.add_node (new_node, lowest_cost_node.id());
queue (new_node.id(), QLine (new_node.x(), new_node.y(), lowest_cost_node.x(), lowest_cost_node.y()));
for (int j = 0; j < X_near.size(); ++j)
{
if (obstacle_free(*X_near[j], new_node) && (new_node.cost() + T.distance (new_node, *X_near[j])) < X_near[j]->cost())
{
queue (X_near[j]->id(), QLine (new_node.x(), new_node.y(), X_near[j]->x(), X_near[j]->y()));
T.update_parent (*X_near[j], new_node.id());
T.rewire_tree (X_near[j]->id());
}
}
}
}
emit finished();
请注意,
T
是代表Tree的类。它由一些允许添加节点,搜索最近节点的方法等组成。它具有QList<cnode>
作为私有成员,用于存储树的节点。 cnode
是由两个坐标(id,父级,成本,子级列表)组成的结构。 最佳答案
解决方案与往常一样-避免频繁排队,因为它们很慢。排队的连接是粗糙的结构,因此可以使用。
批处理工作。在您的方案中,您可以将计算的行聚合到一个容器中,只有在达到某个阈值时,才将该容器传递给主线程以绘制/更新这些行。阈值可以是计数,时间或两者的组合,如果要更新的结果很少,则不希望不更新。您将需要扩展设计以拆分while
循环,使其在线程事件循环中运行而不是阻塞,以便您可以定期汇总和传递更新-与this类似。对于需要花费时间的工作人员来说,这始终是一个好主意-您可以监视进度,取消,暂停以及各种方便的工作。
那两条线看起来很腥:
edges.removeAt(i);
edges.insert (i, scene->addLine (l));
然后,您删除然后插入-这是潜在的昂贵重新分配的邀请,即使没有重新分配,也将涉及不必要的复制。除了删除和插入外,您只需替换该索引处的元素即可。
在您的情况下,您可能会省略拆分实际的while循环。只是不要在循环中发出,而是执行以下操作(伪代码):
while(...) {
...
queue(new line)
...
queue(update line)
...
queue(final flush)
}
void queue(stuff) {
stuffBuffer.append(stuff)
if (stuffBuffer.size() > 50 || final_flush) {
emit do_stuff(stuffBuffer) // pass by copy
stuffBuffer.clear() // COW will only clear stuffBuffer but not the copy passed above
}
}
或者,如果这会让您感觉更好:
copy = stuffBuffer
stuffBuffer.clear()
emit do_stuff(copy)
这样,在发出副本之前,将两个容器与共享数据分离。
编辑:经过长时间的讨论,我最终提出了对设计的一些更改以提高性能(排队的连接只是性能问题的一个方面):
缓解图形场景-在“每行一个项目”和“所有行一个项目”解决方案之间找到折衷方案,其中每个项目都处理其直接子代的线条图,从而平衡了将项目添加到场景的CPU时间并根据数据更改重新绘制项目。
禁用自动场景更新,而是显式控制场景更新,这样就不会为每个微小的更改更新场景。
批量聚合视图命令,并以固定的时间间隔提交工作缓冲区,以避免排队的信号开销。
关于c++ - Qt槽位调用过于频繁,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/34582612/