Странное поведение функции Sleep (), используемой в repeat до Delphi - PullRequest
5 голосов
/ 11 марта 2012

У меня есть функция, которая реагирует на нажатие кнопки. Когда я нажимаю на кнопку, она должна начать повторять и записывать значения в массив и показывать их в метках на главной форме. Проблема с функцией сна - есть какая-то ошибка или что-то еще, потому что, когда я нажимаю на кнопку, она ждет довольно долго, а затем, наконец, начинает действие, но очень быстро. Давайте посмотрим на мой код. Спасибо за советы.

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 ]

19 голосов
/ 12 марта 2012

Не используйте функцию Sleep в потоке графического интерфейса, поскольку вы блокируете обычную обработку сообщений и ваше приложение ведет себя странно.

Неясно, почему вы используете Sleep в своем коде.Возможно, вам следует обновить метки из OnTimer обработчика событий компонента TTimer вместо использования цикла.

1 голос
/ 21 декабря 2013

Я использовал два таймера для «медленного» обновления некоторых значений (чтобы пользователь мог видеть ход выполнения) без замедления работы приложения или, в худшем случае, системы.

//  "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 голосов
/ 13 марта 2012

Мне сложно сказать не использовать Sleep, поскольку я сам все время использую его, но Application.ProcessMessages на самом деле опасное решение, особенно при использовании в цикле.Я не уверен, какую информацию вы отображаете (так как я не распознаю язык), но похоже, что вы делаете некоторые преобразования из Float в String.Хотя эти преобразования выполняются за доли секунды, сложите их все вместе, и вы можете выполнить длительную операцию.И предположим, что вы решили добавить еще одно значение для обновления, которое требует некоторых вычислений (например, байтов в секунду при передаче файла).Это преобразование добавит немного больше времени для этой операции, и, прежде чем вы узнаете об этом, вы можете получить обновление пользовательского интерфейса, которое занимает полсекунды (что кажется недолгим, но когда дело доходит до использования процессора, это довольнонагрузка).

Поэтому я бы предложил использовать поток для выполнения всех этих преобразований, вычислений и т. д. и инициировать события по мере необходимости при изменении этой информации.Безусловно, поток определенно будет немного сложнее, чем другие предлагаемые здесь решения.Но использование потока может также принести большую пользу.Вся ваша тяжелая работа может быть выполнена в фоновом режиме, в то время как ваше приложение по-прежнему реагирует идеально.Имейте в виду, что использование потока может быть очень сложным, особенно когда речь идет об обновлениях пользовательского интерфейса.

Есть несколько способов создать поток, но я постараюсь сделать это простым ...

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.Всякий раз, когда любое значение отличается от своего первоначального (или старого) значения, оно вызывает это событие.Я также настоятельно рекомендую поместить любой поток обработки, который может иметь эти значения, в первую очередь, в поток.Возможности многопоточности огромны и доказывают большое преимущество в приложениях, в которых много чего происходит.

Обратите внимание, что приведенный выше код является просто примером, напечатанным непосредственно на этом сайте, и не тестируется,Это просто чтобы дать вам представление о том, как реализовать поток для обновления.

0 голосов
/ 12 марта 2012

Если вам нужно использовать функцию типа задержки или «сна», вы можете использовать процедуру с 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;
...