【问题标题】:VirtualStringTree updating data using cache systemVirtualStringTree 使用缓存系统更新数据
【发布时间】:2011-07-07 09:31:08
【问题描述】:

嗯,我正在使用 VirtualStringTree 来创建一种进程管理器...

我遇到了麻烦,因为使用设置为 1000 毫秒的计时器更新树(cpu 使用率太高,无法让我的应用程序检索大量数据(填充大约 20 列)。

所以我想知道如何构建一种缓存系统,以便只有在发生更改时才能更新树,我猜这似乎是大幅减少我的应用程序的 cpu 使用率的关键?

截图:

type
  TProcessNodeType = (ntParent, ntDummy);

  PProcessData = ^TProcessData;

  TProcessData = record

   pProcessName : String;
   pProcessID,
   pPrivMemory,
   pWorkingSet,
   pPeakWorkingSet,
   pVirtualSize,
   pPeakVirtualSize,
   pPageFileUsage,
   pPeakPageFileUsage,
   pPageFaults : Cardinal;
   pCpuUsageStr: string;
   pIOTotal: Cardinal;
...

  end;

如果我的应用程序启动,我会用所有正在运行的进程填充树。 请记住,这只调用一次,稍后当应用程序运行时,我会收到新进程或通过 wmi 终止的进程的通知,因此我不需要稍后在计时器中调用以下过程来更新树...

procedure FillTree;
begin
var
  NodeData: PProcessData;
  Node: PVirtualNode;
  ParentNode: PVirtualNode;
  ChildNode: PVirtualNode;
  Process: TProcessItem;
  I : Integer;
begin
   ProcessTree.BeginUpdate;
   for I := 0 to FRunningProcesses.Count - 1 do
  begin
    Process := FRunningProcesses[i];

    NodeData^.pProcessID := ProcessItem.ProcessID;
    NodeData^.pProcessName := ProcessItem.ProcessName;

...

我有一个类,它将检索我想要的所有数据并将其存储到树中,例如:

var
  FRunningProcesses: TProcessRunningProcesses;

因此,如果我想枚举所有正在运行的进程,我只需给它一个如下调用:

  // clears all data inside the class and refills everything with the new data... 
  FRunningProcesses.UpdateProcesses;

问题从这里开始,我列举了所有内容,而不仅仅是已经更改的数据,这些数据非常占用 CPU:

procedure TMainForm.UpdateTimerTimer(Sender: TObject);
var
  NodeData: PProcessData;
  Node : PVirtualNode;
  Process: TProcessItem;
  I: Integer;
begin
   for I := 0 to FRunningProcesses.Count - 1 do
   begin
      Application.ProcessMessages;

      Process := FRunningProcesses[I];

      // returns PVirtualNode if the node is found inside the tree
      Node := FindNodeByPID(Process.ProcessID);

      if not(assigned(Node)) then
      exit;

      NodeData := ProcessVst.GetNodeData(Node);

      if not(assigned(NodeData)) then
       exit;

     // now starting updating the tree 
     // NodeData^.pWorkingsSet := Process.WorkingsSet; 
....

基本上,计时器仅用于 cpu 使用情况以及我可以从以下进程中检索的所有内存信息:

  • 私有内存
  • 工作集
  • 峰值工作组
  • 虚拟大小
  • 页面文件使用
  • 页面文件使用高峰
  • 页面错误
  • CPU 使用率
  • 线程数
  • 句柄计数
  • GDI 句柄计数
  • 用户句柄计数
  • 总 CPU 时间
  • 用户 CPU 时间
  • 内核 CPU 时间

所以我认为上述数据必须被缓存并以某种方式进行比较,如果它发生了变化,或者只是想知道如何以及什么最有效?

【问题讨论】:

    标签: delphi caching virtualtreeview tvirtualstringtree


    【解决方案1】:

    您只需要更新当前可见的节点中的数据。 您可以使用 vst.getfirstvisible vst.getnextvisible 遍历这些节点。

    第二种方法也很简单。 使用对象而不是记录。 sample code of object usage

    对不同的值使用 getter。 那些 getter 查询进程的值。 也许你需要一个限制。每秒刷新一次数据。

    现在您只需要将 vst 设置为每秒失效状态即可。

    vst.invalidate
    

    这迫使 vst 重新绘制可见区域。

    但只有当您的数据没有按任何变化的值排序时,所有这些才有效。 如果有必要,您需要更新所有记录,这是您的瓶颈 - 我认为。 记住 COM 和 WMI 比纯 API 慢得多。 避免(慢)循环并使用分析器来查找慢部分。

    【讨论】:

    • wmi 性能不好已经不是什么秘密了。另一方面,我不知道有任何 api 会告诉你进程是死了还是被创建了。

      我不想每秒钟刷新我的整个列表,只是为了一个创建或终止的进程,并且只有这个进程被添加或删除到树中而不是任何时候。
      我实际上不知道有任何 api 会告诉您有关进程创建或终止的信息,除非您想挂接 csrss 或拧紧驱动程序并注册 PsSetCreateProcessNotifyRoutine 我绝对不能:(
    • @stOrM - 那么你需要放慢刷新速度。我也将从读取进程数据的分离线程开始。如果与最后一个流程数据存在差异,则更新节点(使用消息同步),否则什么也不做。你也不需要刷新整个VT,不要调用Application.ProcessMessages;它可能会闪烁很多。
    • @daemon_x 到目前为止听起来不错......只是不知道如何处理我的 processcollection 中的 pvirtualnode ,我稍后需要将其存储在树记录中(也许对我来说还早关注:))
    • @stOrM - 想法是您将拥有一个单独的线程,它将遍历您的进程集合并根据当前值检查最后收集的值。如果您注意到变化,那么您会说;很酷我在第 n 条记录中的 PageFileUsage 有一个更改(相应的节点将存在),我需要将它刷新给用户。因此,您的工作线程将数据提供给主线程并调用TreeView.InvalidateNode,它将仅刷新此节点。我不确定什么是最有效的,但我在这里看到过很多这样的解决方案。
    • @stOrM - 我在问类似的here,我用临界区锁解决了这个问题。
    【解决方案2】:

    我建议您将 VT 的节点数据直接指向 TProcessItem。

    专业人士:

    1. 摆脱FindNodeByPID。只需更新所有项目 FRunningProcesses,然后拨打VT.Refresh。当过程是 终止,从FRunningProcesses中删除相应的项目。 目前,您在FindNodeByPID 中进行了相当昂贵的搜索,其中 你遍历所有 VT 节点,检索它们的数据并检查 PID。
    2. 摆脱你拥有的Process := FRunningProcesses[I] 整个 TProcessData 记录的不必要的数据副本(顺便说一句,即 无论如何都应该这样做,而是使用指针)。
    3. 摆脱整个 // now starting updating the tree 块。
    4. 一般而言,通过此更改,您可以减少多余的实体,这对应用程序更新和调试非常有用。

    缺点:

    1. 您必须使 VT 和 FRunningProcesses 保持同步。但这很微不足道。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-14
      • 1970-01-01
      • 2012-07-07
      • 1970-01-01
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多