我有一个工作线程,可以处理繁重和长时间的计算(最多十分之一秒)。这些计算产生数千个QLine
s,代表动态增长树的边缘。
这些边可以随时修改,因为它们通过检查由距离表示的成本来连接树的节点。
我想要包含边缘的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
是代表树的类。它由允许添加节点,搜索最近的节点等的一些方法构成。它具有QList<cnode>
作为私有成员,存储树的节点。 cnode
是由两个坐标构成的结构,id,父母,成本,子女列表。
答案 0 :(得分:2)
解决方案与往常一样 - 避免频繁排队连接,因为这些连接非常慢。排队连接是粗粒度构造,因此可以这样使用。
批量处理工作。在您的方案中,您可以聚合容器中的计算行,并且只有当它达到某个阈值时,才将该容器传递给主线程以绘制/更新行。阈值可以是计数,时间或两者的组合,如果只有少量结果需要更新,则不希望不更新。您将需要扩展您的设计以将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时间之间取得平衡用于向场景添加项目和重新绘制数据更改项目。
禁用自动场景更新,而是显式控制场景更新,这样就不会为每一个微小的变化更新场景。
批量聚合查看命令并以固定间隔提交工作缓冲区,以避免排队信号开销。