对于我来说,很难说不要使用Sleep
,因为我自己经常使用它,但Application.ProcessMessages
实际上是一个危险的解决方案,特别是在循环中使用时。我不确定您显示了什么信息(因为我不认识该语言),但看起来您正在执行一些从Float到String的转换。尽管这些转换在瞬间完成,但将它们全部加起来,您会得到一个漫长的操作。而且,假设您决定添加另一个需要一些计算的值进行更新(例如文件传输中的每秒字节数)。这种转换将为该操作增加更多时间,而在您意识到之前,您可能会得到需要占用相当大处理器资源的UI更新,甚至需要半秒钟的时间。
因此,我建议使用线程来执行所有这些转换、计算等操作,并在信息发生更改时根据需要触发事件。现在,相比其他建议的解决方案,使用线程肯定会更复杂,毫无疑问。但使用线程也可以带来许多好处。您所有的重活都可以在后台完成,而您的应用程序仍然可以完美地响应。请记住,在涉及UI更新时,使用线程可能非常棘手。
有几种方法可以创建线程,但我会尝试让它简单...
type
TMyThread = class;
TMyThreadEvent = procedure(Sender: TObject; const Val1, Val2: String) of object;
TMyThread = class(TThread)
private
FValue1: Integer;
FValue2: Integer;
FString1: String;
FString2: String;
FOnChange: TMyThreadEvent;
procedure SYNC_OnChange;
protected
procedure Execute; override;
public
constructor Create;
property Value1: Integer read FValue1 write FValue1;
property Value2: Integer read FValue2 write FValue1;
property String1: String read FString1;
property String2: String read FString2;
property OnChange: TMyThreadEvent read FOnChange write FOnChange;
end;
...
constructor TMyThread.Create;
begin
inherited Create(False);
FValue1 := '0';
FValue2 := '0';
end;
procedure TMyThread.Execute;
var
S1, S2: String;
DoChange: Bool;
begin
DoChange:= False;
FValue2:= DoSomeBigCalculation(FValue1);
S1:= FormatFloat('#,##0.#', FValue1);
S2:= FormatFloat('#,##0.#', FValue2);
if (S1 <> FString1) then begin
FString1:= S1;
DoChange:= True;
end;
if (S2 <> FString2) then begin
FString2:= S2;
DoChange:= True;
end;
if DoChange then
Synchronize(SYNC_OnChange);
end;
procedure TMyThread.SYNC_OnChange;
begin
if assigned(FOnChange) then
FOnChange(Self, FString1, FString2);
end;
现在,要使用它,您需要根据需要设置 Integer
属性。确保将 OnChange
事件设置为具有上述 TMyThreadEvent
类型参数的过程。每当任何值与其原始(或旧)值不同时,都会触发此事件。我还强烈建议您将首先产生这些值的任何处理代码放在一个线程内。多线程的可能性是广泛的,并在具有许多功能的应用程序中证明了巨大的优势。
请注意,我上面的代码只是直接键入此网站的示例,未经测试。这只是为了让您了解如何实现线程来更新内容。
Sleep(X);
这样的简单代码行,其中X是一个像60,000这样的巨大数字,那么应用程序将会在1分钟(60,000毫秒)内被冻结。 Uninterruptable 大致翻译为“无法告诉应用程序停止Sleep
命令”。 - Jerry Dodge