Delphi 如何确定主线程不响应全线程库的原因?

Delphi 如何确定主线程不响应全线程库的原因?,delphi,freeze,omnithreadlibrary,otl,Delphi,Freeze,Omnithreadlibrary,Otl,平台:带有VirtualTreeView SVN 5.1.0和OmniThreadLibrary 3 SVN和Delphi XE2的Delphi 最初我认为问题在于VirtualTreeView。我需要每隔1s或更短的时间向VST添加一个节点,但似乎很快或更晚CPU速率将达到50%或更高,直到整个应用程序完全没有响应 var FAbort:Boolean; ..... procedure TrmMain.btnAddNodeClick(Sender: TObject); b

平台:带有VirtualTreeView SVN 5.1.0和OmniThreadLibrary 3 SVN和Delphi XE2的Delphi

最初我认为问题在于VirtualTreeView。我需要每隔1s或更短的时间向VST添加一个节点,但似乎很快或更晚CPU速率将达到50%或更高,直到整个应用程序完全没有响应

  var FAbort:Boolean;
  .....

  procedure TrmMain.btnAddNodeClick(Sender: TObject);
    begin
      while not FAbort do
      begin
        VstMain.RootNodeCount:= VstMain.RootNodeCount + 1;
        Sleep(10);
        Application.ProcessMessages;
      end;
    end;
有人能帮忙吗?蒂亚

编辑:问题似乎来自于OTL。当使用上述代码时,将应用程序最小化,CPU始终小于1%,甚至将10ms睡眠更改为1ms

但是,下面的代码将重现困扰我的问题

procedure TForm1.btn5Click(Sender: TObject);
var
  I: Integer;
begin
  for I := 0 to 1 do
    CreateTask(
      procedure(const Task: IOmniTask)
      begin
        while not FAbort do
        begin
          Task.Comm.Send(1, 0);
          Sleep(10);
        end;
      end).OnMessage(
      procedure(const Task: IOmniTaskControl; const Msg: TOmniMessage)
      begin
        vst1.AddChild(nil);
      end).Run;
end;
PS:为了避免溢出到OTL默认1000队列大小,我在每个线程中都有一个锁,在下一个Task.Comm.Send操作之前等待添加节点完成

PPS:这里的10毫秒只是为了快速重现问题,而不是真实情况。所以别费心问为什么


好的,结论是:如果需要定期更新此节点,则不要在单个节点上添加太多节点,节点越多,更新它们的cpu时间就越多。

如果使用AddChild()函数添加节点,情况可能会比访问RootNodeCount属性更好

例如:

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
begin
  Node := MyTree.AddChild( nil );
  // fill in details with GetNodeData( Node );

end;
更好的方法是:使用计时器并尝试在每个时间间隔添加一些项目:

procedure TMyForm.OnTimer( Sender: TObject );
begin
  AddToList( ... );
end;

procedure TMyForm.OnTimer( Sender: TObject );
var
  Node: PVirtualNode;
  Item: <Some iterator>;
begin
  MyTree.BeginUpdate();
  try
    for Item in <somelist> do begin
      Node := MyTree.AddChild( nil );
      // fill in details with GetNodeData( Node );
    end;
  finally
    MyTree.EndUpdate();
  end; 
end;
程序TMyForm.OnTimer(发送方:TObject);
开始
AddToList(…);
结束;
程序TMyForm.OnTimer(发送方:ToObject);
变量
节点:PVirtualNode;
项目:;
开始
MyTree.BeginUpdate();
尝试
对于do begin中的项目
节点:=MyTree.AddChild(nil);
//使用GetNodeData(节点)填写详细信息;
结束;
最后
EndUpdate();
结束;
结束;

在我看来,您不应该在基础模型更改时同步更新视图,尤其不是每次都同步更新

VirtualTreeView是一个可视控件。人类不需要实时看到树的更新,任何超过每秒3次的更新都是浪费。所以不要这样做

相反,请更新您的模型(对象、类),设置通知标志,然后(从TTimer)执行VirtualStringTree.RootNodeCount的异步单次更新,最多每秒更新3次。(每333毫秒设置一个以上的更新标志会导致333毫秒的最小等待时间,直到它真正更新。)这是我的任意用户界面“比这更快,只是闪烁和搅动,没有任何用处”常数


Delphi自己的开发人员在VirtualTreeView中遇到了这个问题,我知道是因为我记录了涉及的QC错误。如果在Delphi2009中执行了足够多的“OutputDebugString”消息,IDE将变得无响应。为什么?因为他们做了你正在做的事。不要这样做。我并不是说用户点击会导致屏幕刷新前等待333毫秒。我的意思是,一些连续生成树内容的进程应该每秒最多只通知树的“视图控制器”3次更改。

您能添加更多的坏消息吗?使用Sleep plus Application.ProcessMessages进行无休止的循环?这是一首糟糕的交响乐。如何添加访问冲突和内存泄漏?认真地你在想什么?添加TTimer,并从计时器(而不是在循环中)添加节点。GUI应用程序不是你可以用无休止的循环来控制的。学会热爱事件驱动编程,理解Sleep(X)和Application.ProcessMessages不是解决问题的方法。冷静点,我只是想重现问题,真正的代码与我上面提到的不同。添加节点事件仅在出现新日志时发生。连接探查器并找出它在哪里花费时间。请记住,内部数据结构是一个双链表,因此随着结构的增长,遍历它需要更长的时间。如果您描述行为,然后发布伪代码而不是真实代码,则很难理解问题。为什么必须每10毫秒添加一个节点?谁能每隔10毫秒读取一个新条目?我同意在RootNodeCount>1000的值下每秒扩展
RootNodeCount
100次可能会变得越来越昂贵。可能会发生一些疯狂的重建整棵树的事情。但唯一能知道的方法是我们聪明的原创帖子,让那个人及时跑过去,自己去发现。P:谢谢,我明白你们的意思了。DelphiIDE的日志视图确实使用VST,并且确实存在这个问题。IDE修复包的作者Andreas Hausladden添加了一个间隔为70ms或80ms的计时器来更新VST,并修复了这个问题。如果单个节点(在我的例子中是RootNode)的子节点增长太快,太多,那么每次更新UI的开销就太大了。在我的代码中,我简单地添加了一个限制,即10000,如果子节点超过该限制,则在清除所有节点之前保存它,最后,这解决了我的问题。正如您所提到的,周期性地添加一组节点更有效。抱歉,我没有让@正常工作。当我输入你的名字时,它会吃掉它。如果这解决了你的问题,那么你应该编辑你的标题和问题,以便将来的人更容易找到它。