VirtualStringTree使用缓存系统更新数据

时间:2011-07-07 09:31:09

标签: delphi caching virtualtreeview tvirtualstringtree

好吧,我正在使用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;

...

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

var
  FRunningProcesses: TProcessRunningProcesses;

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

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

问题从这里开始,而我列举了所有内容,而不仅仅是那些已经发生变化且非常密集的数据:

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使用和我可以从以下过程检索的所有内存信息都需要定时器:

  • Priv.Memory
  • 工作集
  • 高峰工作集
  • 虚拟尺寸
  • PageFile用法
  • 峰值页面文件使用情况
  • 页面错误
  • Cpu Usage
  • 主题计数
  • 处理计数
  • GDI Handle Count
  • 用户处理计数
  • 总CPU时间
  • 用户Cpu时间
  • 内核CPU时间

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

2 个答案:

答案 0 :(得分:0)

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

第二种方式也很容易。 使用对象而不是记录。 sample code of object usage

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

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

vst.invalidate

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

但只有当您的数据未按任何更改值排序时,所有这些都有效。 如果这需要你需要更新所有记录,这是你的瓶颈 - 我想。 记住COM和WMI比纯API慢得多。 避免(慢)循环并使用分析器找到慢速部件。

答案 1 :(得分:0)

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

Pro的:

  1. 摆脱FindNodeByPID。只需更新所有项目 FRunningProcesses,然后致电VT.Refresh。当过程是 已终止,请从FRunningProcesses删除相应的项目。 目前,您在FindNodeByPID的搜索位置非常昂贵 循环遍历所有VT节点,检索其数据并检查 PID。
  2. 摆脱你所拥有的Process := FRunningProcesses[I] 整个TProcessData记录的不必要的数据副本(顺便说一下,那个 无论如何应该使用指针)。
  3. 摆脱整个// now starting updating the tree区块。
  4. 通常,通过此更改,您可以减少多余的实体,这对应用程序更新和调试非常有用。
  5. 反对的:

    1. 您必须保持VT和FRunningProcesses同步。但那是非常微不足道的。