函数Sleep()的奇怪行为在Delphi中重复使用

时间:2012-03-11 14:59:07

标签: delphi delphi-7 sleep repeat

我有按钮点击的反应功能。当我点击按钮时,它应该开始重复并从数组中写入值并在主窗体上的标签中显示它们。问题是功能睡眠 - 有一些错误或什么的,因为当我点击按钮它等待很长时间然后它最终开始动作但很快。我们来看看我的代码。感谢建议。

procedure TForm1.ButtonMereniClick(Sender: TObject);
var
  iterator: Integer;
begin      
  iterator := 1;
  repeat       
    //write some values stored int arrays to labels on form
    LabelTeplota.Caption:='Teplota: '+FloatToStr(poleTeplota[iterator]);
    LabelVlhkost.Caption:='Vlhkost: '+FloatToStr(poleVlhkost[iterator]);
    LabelTlak.Caption:='Atmosférický tlak: '+FloatToStr(poleTlak[iterator]);
    LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]);
    LabelRychlost.Caption:='Rychlost větru: '+FloatToStr(poleRychlost[iterator]);
    LabelIterator.Caption:='iterator: '+IntToStr(iterator);
    Sleep(500);//should be 5000 
    Inc(iterator);
  until iterator = 20;
end;

4 个答案:

答案 0 :(得分:19)

不要在GUI线程中使用Sleep函数,因为您阻止了正常的消息处理,并且您的应用程序行为异常。

目前尚不清楚为什么在代码中使用Sleep。您可能应该更新OnTimer组件的TTimer事件处理程序中的标签,而不是使用循环。

答案 1 :(得分:1)

我使用两个计时器“慢慢”更新一些值(以便用户可以看到进度),而不会减慢应用程序的速度,或者在最坏的情况下,系统。

//  "weights" for old and new value; I was working on integers
var
  Wa: integer =  1;
  Wb: integer =  9;
  Wt: integer = 10;

//  interval of 1000 ms, of course
procedure frmMain.T1000Timer(Sender: TObject);
begin
  Wa:= 1;
  nValue:= calculate_new_value;
end;

//  100 ms interval
procedure frmMain.T100Timer(Sender: TObject);
begin
  Wb:= Wt -Wa;
  //  displayed value, ie gauge.progress, or something.Value, etc.
  sam.Value:= Round( (Wb *sam.Value +Wa *nValue) /Wt );
  if Wa < Wt then Inc(Wa);
end;

答案 2 :(得分:0)

如果必须使用延迟或“睡眠”类型功能,则可以使用ProcessMessages的过程。使用它有一些优点和缺点,但我已经成功地在很多场合没有任何不良影响。我知道这里有其他人可以更好地评论ProcessMessages。

Procedure Delay(MSecs: Cardinal);
var
 FirstTick, CurrentTick : Cardinal;
 Done : Boolean;
begin
 Done := FALSE;
 FirstTick := GetTickCount;
 While Not Done do
  begin
   Application.ProcessMessages;
   CurrentTick := GetTickCount;
   If Int64(CurrentTick) - Int64(FirstTick) < 0 Then
    begin
     If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then
      Done := TRUE;
       End
        Else
         If CurrentTick - FirstTick >= MSecs Then
          Done := TRUE;
  end;
end;

// Below for a service

procedure YourSvrSvc.ProcessMessages;
var
  Msg: TMsg;
begin
  if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
  begin
    TranslateMessage(Msg);
    DispatchMessage(Msg);
  end;
end;

Procedure YOURSvrSvc.Delay(MSecs: Cardinal);
var
 FirstTick, CurrentTick : Cardinal;
 Done : Boolean;
begin
 Done := FALSE;
 FirstTick := GetTickCount;
 While Not Done do
  begin
   YOURSvrSvc.ProcessMessages;
   CurrentTick := GetTickCount;
   If Int64(CurrentTick) - Int64(FirstTick) < 0 Then
    begin
     If CurrentTick >= (Int64(FirstTick) - High(Cardinal) + MSecs) Then
      Done := TRUE;
       End
        Else
         If CurrentTick - FirstTick >= MSecs Then
          Done := TRUE;
  end;
end;

答案 3 :(得分:0)

我很难说不使用Sleep因为我自己一直使用它,但Application.ProcessMessages实际上是一个危险的解决方案,尤其是在循环中使用时。我不确定你显示的是什么信息(因为我不认识这种语言),但看起来你正在做一些从Float到String的转换。虽然这些转换是在瞬间执行的,但将它们全部添加在一起,您可能会进行冗长的操作。并且假设您决定添加另一个要更新的值,这需要一些计算(例如文件传输中的每秒字节数)。这种转换会给这个操作增加一点时间,在你知道它之前,你可能会花费半秒钟的UI更新(这似乎不长,但是当涉及处理器使用时,这是相当的负载)。

因此,我建议使用Thread执行所有这些转换,计算等,并在信息发生变化时根据需要触发事件。现在,毫无疑问,线程肯定会比其他建议的解决方案复杂一点。但是使用线程也意味着很多好处。当您的应用程序仍然完美响应时,您可以在后台完成所有繁重的工作。请记住,使用线程可能非常棘手,特别是在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); //Some random big calculation
    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类型参数的过程。只要任何值与其原始(或旧)值不同,它就会触发此事件。我还强烈建议您首先生成这些值的处理代码放在一个线程中。多线程的可能性是巨大的,并且在其中有很多进展的应用程序中证明是一个很大的优势。

请注意,我的上述代码只是直接输入本网站的样本,未经过测试。这只是为了让您了解如何实现一个线程来进行更新。