嗯,我使用VirtualStringTree来创建一种进程管理器…
我遇到了麻烦,因为更新树时将计时器设置为1000ms(对于检索大量数据(填充大约20列)的应用程序来说,cpu使用率太高)。
所以我想知道如何构建一种缓存系统,所以我可以更新树只有当发生变化时,我猜似乎是关键减少了我的应用程序的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使用情况和我可以从进程中检索的所有内存信息,如:
- Priv。内存
- 工作集
- 峰值工作集 虚拟大小
- 页面文件使用
- 页面文件使用峰值
- 页面错误 Cpu使用线程数
- 处理数
- GDI句柄计数
- 用户句柄数
- Cpu总时间
- 用户Cpu时间
- 内核Cpu时间
所以我认为上面的数据必须缓存和比较,如果它改变或不只是想知道如何和什么将是最有效的?
您只需要更新当前可见的节点中的数据。您可以使用vst.getfirstvisible
vst.getnextvisible
来遍历这些节点。
第二种方法也很简单。使用对象而不是记录。对象使用示例代码
为不同的值使用getter。这些getter向进程查询值。也许这里需要一个极限。每秒钟刷新一次。
现在你只需要每秒钟将VST设置为无效状态。
vst.invalidate
这迫使VST重新绘制可见区域。
,但所有这些只在数据不按任何变化值排序时才有效。如果有必要,你需要更新所有的记录,我认为这是你的瓶颈。记住,COM和WMI比纯API慢得多。避免(慢)循环,并使用分析器来查找慢的部分。
我建议您将VT的节点数据直接指向TProcessItem。
职业:
- 去除
FindNodeByPID
。更新所有的项目FRunningProcesses
,然后调用VT.Refresh
。当过程是终止,从FRunningProcesses
中删除相应项。目前你有相当昂贵的搜索在FindNodeByPID
你遍历所有VT节点,检索它们的数据并检查PID。 - 去掉
Process := FRunningProcesses[I]
整个TProcessData记录的不必要的数据副本(顺便说一句,那应该这样做,使用指针代替)。 - 删除整个
// now starting updating the tree
块 一般来说,通过这个改变,你减少了多余的实体,这对应用程序的更新和调试是非常好的。
反对:
- 你必须保持VT和runningprocesses同步。但这是相当微不足道的。