Delphi VirtualStringTree使用缓存系统更新数据
嗯,我正在使用VirtualStringTree创建一种流程管理器 我遇到了麻烦,因为更新树时计时器设置为1000ms(cpu使用率太高,无法让我的应用程序检索大量数据(填充大约20列) 所以我想知道如何构建一种缓存系统,这样只有当某些东西发生了变化时我才能更新树,我猜这似乎是降低我的应用程序cpu使用率的关键 剪报: 如果我的应用程序启动,我将用所有正在运行的进程填充树。 请记住,这只调用一次,稍后当应用程序运行时,我收到新进程或通过wmi终止的进程的通知,因此我不需要稍后在计时器中调用以下过程来更新树Delphi VirtualStringTree使用缓存系统更新数据,delphi,caching,virtualtreeview,tvirtualstringtree,Delphi,Caching,Virtualtreeview,Tvirtualstringtree,嗯,我正在使用VirtualStringTree创建一种流程管理器 我遇到了麻烦,因为更新树时计时器设置为1000ms(cpu使用率太高,无法让我的应用程序检索大量数据(填充大约20列) 所以我想知道如何构建一种缓存系统,这样只有当某些东西发生了变化时我才能更新树,我猜这似乎是降低我的应用程序cpu使用率的关键 剪报: 如果我的应用程序启动,我将用所有正在运行的进程填充树。 请记住,这只调用一次,稍后当应用程序运行时,我收到新进程或通过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时间
因此,我认为,如果上述数据发生了更改,则必须以某种方式对其进行缓存和比较,而不仅仅是想知道如何以及什么最有效?您只需更新当前可见节点中的数据。 您可以使用
vst.getfirstvisible
vst.getnextvisible
对这些节点进行迭代
第二种方法也很简单。
使用对象而不是记录
对不同的值使用getter。
这些getter查询进程中的值。
也许您需要在这里设置一个限制。仅每秒刷新数据
vst.invalidate
现在,您只需要每秒将vst设置为无效状态
vst.invalidate
这迫使vst重新绘制可见区域
但是,只有当您的数据没有按任何变化的值排序时,所有这些才起作用。
如果这是必要的,你需要更新所有记录,这是你的瓶颈-我想。
记住COM和WMI比纯API慢得多。
避免(慢速)循环,并使用探查器查找慢速部分。我建议您将VT的节点数据直接指向TProcessItem 专业人士:
FindNodeByPID
。只需更新
FRunningProcesses
,然后在进程启动时调用VT.Refresh
终止,从frunningprocesss
中删除相应的项。
目前,您在FindNodeByPID
where中有相当昂贵的搜索
循环遍历所有VT节点,检索它们的数据并检查
PIDProcess:=frunningprocesss[I]
整个TProcessData记录的不必要的数据副本(顺便说一句,即
无论如何都应该这样做,请使用指针)//现在开始更新树
块wmi的性能不好已经不是什么秘密了。另一方面,我不知道有什么api可以告诉你进程是否已死亡或已创建。
我不想只为已创建或已终止的进程每秒刷新整个列表,并且只将此进程添加或删除到树中,而不是在任何时候。
现在,任何api都会告诉您进程的创建或终止,除非您想挂接csrss或编写驱动程序并注册PsSetCreateProcessNotifyRoutine,我绝对不能:(@stOrM-那么你需要减慢刷新速度。我还将从读取进程数据的分离线程开始。如果与上一个进程数据存在差异,则更新节点(使用消息同步)否则什么也不做。您也不需要刷新整个VT,也不需要调用
应用程序。ProcessMessages
;它可能会闪烁很多。@daemon_x到目前为止听起来不错……只是不知道如何处理processcollection中的pvirtualnode,我以后需要将它存储在树记录中(也许我现在开始关注:)@stOrM-想法是你将有一个单独的线程,它将迭代你的进程集合,并根据当前值检查最后收集的值。如果你注意到一个更改,你会说:酷,我在第n条记录中的PageFileUsage有一个更改(将出现相应节点的位置),我需要将其刷新给用户。因此,您的工作线程将数据提供给主线程,并调用TreeView.InvalidateNode
,这将只刷新此节点。我不确定什么是最有效的,但我在这里看到了这么多解决方案。@stOrM-我问了类似的问题,我用关键部分锁解决了这个问题。