如果你没有用过 TAction,或许你不该说你会 Delphi。TAction 大大简化了界面逻辑的关联,加速了项目的开发。不过我在程序中大量使用 TAction 时,无意发现这样一个问题:如果程序的某个窗体里有超过100个 TAction,在运行这个程序时,只要不停的在该窗体上快速移动鼠标,CPU 占用率会猛增到 30% 左右。
为什么会有如此高的 CPU 占用?
使用 Spy++ 调试后发现,一旦鼠标快速在窗体上移动,程序会频繁发送 WM_UPDATE 消息。进一步调试后发现,TContainedAction.Update() 被 TActionManager 频繁调用。正如帮助文档中所写:当应用程序处于空闲状态时,所有的 TxxxxAction.OnUpdate 事件会被触发。由于空闲状态频繁改变,因此 OnUpdate 也就被频繁触发,这个正是造成不当 CPU 占用的真正原因。
如果你的程序中没有使用到 TxxxxAction.OnUpdate,那么你可以屏蔽 TActionManager 去查询并触发 TxxxxAction.OnUpdate。具体实现代码如下:
uses
FastcodePatch {http://fastcode.sourceforge.net/};
procedure TContainedActionUpdateStub;
asm
call TContainedAction.Update;
end;
type
TContainedActionPatch = class(TContainedAction)
public
function Update: Boolean; override;
end;
function TContainedActionPatch.Update: Boolean;
begin
Result := False;
end;
function DisableTActionOnUpdate(ActnList: TActionList): Boolean;
var
I: Integer;
begin
Result := True;
for I := 0to ActnList.ActionCount - 1do
if Assigned(ActnList.Actions[I].OnUpdate) then
begin
Result := False;
Break;
end;
if Result then
begin
FastcodeAddressPatch(
FastcodeGetAddress(@TContainedActionUpdateStub),
@TContainedActionPatch.Update);
end;
end;
这段代码的最佳运行位置是在你程序窗体的OnCreate() 事件中。当然,如果你希望彻底解决这个问题,你可以修改ActnList.pas 中的TContainedAction.Update,或者提交申请让CodeGear 来改进这个问题。
本文展示了过多使用TxxxxAction 组件可能会造成应用程序过高的CPU 占用问题。并且提供了补丁代码来解决这个问题。尽管我手上没有最新版本的 Delphi,但是估计这个问题是不会被 CodeGear 修正的。