我有一个Worker Thread,可以处理繁重而漫长的计算(长达十分之一秒)。这些计算产生数千个QLine,代表动态增长的树的边缘。
这些边缘可以随时修改,因为它们通过检查成本(由距离表示)来连接树的节点。
我想要包含边缘的QGraphicsScene的平滑更新。
我尝试了信号和插槽:


工作线程会发出一个信号,因此当缓冲区已满时,该信号会被主线程捕获,从而可以处理该行的更新/绘制
这个信号仍然被主线程捕获,但是似乎它经常被发出,所以QGraphicsViewQLine阻塞了
更改缓冲区大小无关紧要
有替代方法吗?


主插槽是:

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/

10-12 20:42