Strange behaviour of function Sleep() used in repeat until in Delphi

前端 未结 4 1306
走了就别回头了
走了就别回头了 2021-01-19 01:28

I have function which is reaction on button click. When I click on the button it should start repeat and write values form an array and show them in labels on main form. Pro

相关标签:
4条回答
  • 2021-01-19 02:10

    I used two timers to update "slowly" some values (so that the user can see the progress) without slowing down the application or, in the worst case, the system.

    //  "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;
    
    0 讨论(0)
  • 2021-01-19 02:11

    Don't use Sleep function in GUI thread because you are blocking normal message processing and your application behaves strangely.

    It is not clear why you use Sleep in your code. Probably you should update labels from OnTimer event handler of TTimer component instead of using a loop.

    0 讨论(0)
  • 2021-01-19 02:17

    It's difficult for me to say not to use Sleep since I myself use it all the time, but Application.ProcessMessages is in fact a dangerous solution, especially when used in a loop. I'm not sure what information you're displaying (since I don't recognize the language) but it looks like you're doing some conversions from Float to String. Although these conversions are performed in a split second, add them all together and you could come up with a lengthy operation. And suppose you decide to add another value to be updated, which requires some calculation (such as bytes per second in a file transfer). This conversion will add a bit more time onto this operation, and before you know it, you could wind up with a UI update which takes half a second (which doesn't seem long, but when it comes to processor usage, this is quite a load).

    Therefore, I would suggest using a Thread to perform all of these conversions, calculations, etc. and trigger events as needed whenever that information has changed. Now a thread will definitely be a little more complex than the other suggested solutions here, no doubt. But using a thread can mean a great deal of benefits too. All your heavy work can be done in the background while your application is still responding perfectly. Keep in mind that using a thread can be very tricky, specifically when it comes to UI updates.

    There's a few ways to make a thread, but I'll try to make this simple...

    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;
    

    Now to use this, you would set the Integer properties as needed. Make sure you set the OnChange event to a procedure with the parameters of the above TMyThreadEvent type. Whenever any value differs from its original (or old) value, it will trigger this event. I would also highly recommend that whatever processing code you might have which produces these values in the first place be put inside of a thread. The possibilities of multi-threading is vast and prove a great advantage in applications which have a lot going on in them.

    Please note that my above code is just a sample typed directly into this website, and is not tested. It's just to give you an idea of how to implement a thread to do your updating.

    0 讨论(0)
  • 2021-01-19 02:23

    If you must use a delay or "sleep" type function, you can use a procedure with ProcessMessages. There are some plusses and minuses to using it, but I have successfully on many occasions with no ill effects. I know there are others here who can better comment on 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;
    
    0 讨论(0)
提交回复
热议问题