如何确定主线程没有响应 Omni 线程库的原因?
Posted
技术标签:
【中文标题】如何确定主线程没有响应 Omni 线程库的原因?【英文标题】:How to determine cause of main thread not responding with Omni Thread Library? 【发布时间】:2013-01-10 02:59:10 【问题描述】:平台:Delphi 与 VirtualTreeView SVN 5.1.0 & OmniThreadLibrary 3 SVN & Delphi XE2
本来我以为是VirtualTreeView的问题。我需要每隔 1 秒或更短的时间将节点添加到 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 sleep 更改为 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时间越多。
【问题讨论】:
你能再增加一些坏处吗? Endless While 循环,使用 Sleep 和 Application.ProcessMessages?这是Bad的交响曲。添加访问冲突和内存泄漏怎么样?严重地。你在想什么?添加一个TTimer,并从定时器中添加一个节点,而不是在循环中。 GUI 应用程序不是您可以通过无限循环来控制的。学会热爱事件驱动编程,并了解 Sleep(X) 和 Application.ProcessMessages 不是解决问题的方法。 冷静点,我只是想重现问题,真正的代码不像我上面放的那样。添加节点事件仅在有新日志到来时发生。 连接分析器并找出它在哪里花费时间。请记住,内部数据结构是一个双向链表,因此随着结构的增长遍历它需要更长的时间。 如果您描述行为然后发布虚假代码而不是真实代码,那么很难理解问题。 为什么要每10ms添加一个节点?谁能够每 10ms 读取一个新条目? 【参考方案1】:在我看来,当底层模型发生变化时,你不应该同步更新视图,尤其是每次都不要。
VirtualTreeView 是一个可视化控件。人类不需要实时查看树更新,每秒浪费超过 3 倍。所以不要这样做。
相反,更新您的模型(对象、类),设置通知标志,然后(从 TTimer)执行 VirtualStringTree.RootNodeCount 的异步 SINGLE 更新,最多每秒发生 3 次。 (每 333 毫秒设置多个此更新标志会导致最短等待时间为 333 毫秒,直到它实际更新。)这是我的任意用户界面“比这更快,它只是闪烁和流失,没有任何用处”常量.
Delphi 自己的开发人员使用 VirtualTreeView 遇到了这个问题,我知道是因为我记录了所涉及的 QC 错误。如果您在 Delphi 2009 中执行了足够多的“OutputDebugString”消息,IDE 将变得无响应。为什么?因为他们做了你正在做的事。不要这样做。我并不是说用户点击应该导致屏幕刷新前等待 333 毫秒。我的意思是,某些不断生成树内容的进程应该只通知树的“视图控制器”每秒最多 3 次更改。
【讨论】:
P:谢谢,我明白了。 Delphi IDE 的日志视图确实使用了 VST,并且确实存在这个问题。 IDE Fix Pack 的作者 Andreas Hausladen 添加了一个 70ms 0r 80ms 间隔的计时器来更新 VST 并修复了该问题。如果单个节点(在我的例子中是 RootNode)的子节点增长太快而且太多,那么每次更新 UI 的开销就太昂贵了。在我的代码中,我简单地添加了一个限制,即 10,000,如果子节点超过了它,在清除它们之前保存它,最后,这解决了我的问题。正如你提到的,周期性添加一堆更有效. 抱歉,我没有让 @ 工作。当我输入你的名字时,它就会吃掉它。 如果这解决了您的问题,那么您应该编辑您的标题和问题,以便将来的人更容易找到。【参考方案2】:如果您使用 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;
【讨论】:
我同意以 RootNodeCount > 1000 的值每秒扩展RootNodeCount
100 次可能会变得越来越昂贵。可能会发生一些疯狂的重建整棵树。但唯一知道的方法是我们巧妙的原始帖子要求人们在 AQTime 中运行并为自己找出答案。以上是关于如何确定主线程没有响应 Omni 线程库的原因?的主要内容,如果未能解决你的问题,请参考以下文章