2012-03-11 28 views
5

Ho una funzione che è la reazione al clic del pulsante. Quando clicco sul pulsante, dovrebbe iniziare a ripetere e scrivere valori formando una matrice e mostrarli in etichette sul modulo principale. Il problema è con la funzione sleep - c'è qualche bug o qualcosa del genere, perché quando clicco sul pulsante aspetta un tempo piuttosto lungo e poi finalmente avvia l'azione ma molto rapidamente. Diamo un'occhiata al mio codice. Grazie per i consigli.Strano comportamento della funzione Sleep() utilizzato in ripetizione fino a Delphi

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; 
+11

Vivo secondo questa massima: "Se senti il ​​bisogno di usare Sleep(), stai sbagliando". –

+0

@nick Effettivamente. Il mio equivalente è "Non ci sono problemi per i quali il sonno è la soluzione". –

+5

@NickHodges ecc. In che modo, quindi, soddisfi il requisito di una pausa quando vari livelli in profondità su una pila di thread? Lasciami indovinare: 'spendi anni a ri-implementare le specifiche proceduralmente scritte come macchine di stato, solo per poter usare un timer al posto di sleep() chiama'. Non c'è dubbio che i loop di A.P + sleep() nei gestori di GUI sono un abuso particolarmente debole, ma l'argomento 'sleep può essere usato male, quindi non usarlo affatto' è err .. 'difficile da supportare'. –

risposta

19

Non usare Sleep funzione nella discussione GUI, perché si sta bloccando la normale elaborazione del messaggio e l'applicazione si comporta in modo strano.

Non è chiaro il motivo per cui si utilizza Sleep nel codice. Probabilmente dovresti aggiornare le etichette dal gestore di eventi OnTimer del componente TTimer invece di utilizzare un ciclo.

+2

+1 Questa è la risposta corretta. Il blocco del thread della GUI è sempre una brutta mossa. Usa un timer. –

0

Se è necessario utilizzare una funzione di tipo delay o "sleep", è possibile utilizzare una procedura con ProcessMessages. Ci sono alcuni lati positivi e negativi per usarlo, ma ho avuto successo in molte occasioni senza effetti negativi. So che ci sono altri qui che possono commentare meglio 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; 
+5

ProcessMessages ti apre verso le chiamate rientranti al gestore di eventi. –

+0

@David Sì signore! Ho detto che ci sono aspetti positivi e negativi, ma ho pensato che potrebbe andare oltre lo scopo dell'OP. Ma gli ha dato un'opzione. – John

+2

Vedo solo gli svantaggi nell'utilizzo di ProcessMessages. –

0

E 'difficile per me dire di non usare Sleep da quando io lo uso per tutto il tempo, ma Application.ProcessMessages è in realtà una soluzione pericolosa, specialmente se usata in un ciclo. Non sono sicuro di quali informazioni stai visualizzando (poiché non riconosco la lingua) ma sembra che tu stia facendo alcune conversioni da Float a String. Sebbene queste conversioni vengano eseguite in una frazione di secondo, aggiungerle tutte insieme e potresti ottenere una lunga operazione. Supponiamo che tu decida di aggiungere un altro valore da aggiornare, che richiede alcuni calcoli (come i byte al secondo in un trasferimento di file). Questa conversione aggiungerà un po 'più di tempo a questa operazione, e prima che tu te ne accorga, potresti finire con un aggiornamento dell'interfaccia utente che impiega mezzo secondo (il che non sembra lungo, ma quando si parla di utilizzo del processore, questo è abbastanza un carico).

Pertanto, suggerirei di utilizzare una discussione per eseguire tutte queste conversioni, calcoli, ecc. E attivare gli eventi in base alle esigenze ogni volta che le informazioni sono cambiate. Ora un thread sarà sicuramente un po 'più complesso delle altre soluzioni proposte qui, senza dubbio. Ma usare un thread può significare anche una grande quantità di benefici. Tutto il tuo lavoro pesante può essere fatto in background mentre la tua applicazione risponde ancora perfettamente. Tieni presente che l'utilizzo di un thread può essere molto complicato, in particolare quando si tratta di aggiornamenti dell'interfaccia utente.

Ci sono alcuni modi per rendere un filo, ma cercherò di fare questa semplice ...

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; 

Ora per utilizzare questo, è necessario impostare le proprietà Integer come necessario. Assicurati di impostare l'evento OnChange su una procedura con i parametri del tipo TMyThreadEvent precedente. Ogni volta che un valore differisce dal suo valore originale (o vecchio), attiverà questo evento. Raccomando inoltre vivamente che qualsiasi codice di elaborazione che si possa avere e che produca questi valori sia messo al primo posto all'interno di un thread. Le possibilità di multi-threading sono enormi e si rivelano un grande vantaggio per le applicazioni che hanno molto successo in esse.

Si prega di notare che il mio codice sopra è solo un esempio digitato direttamente in questo sito Web, e non è testato. È solo per darti un'idea di come implementare un thread per fare il tuo aggiornamento.

+0

PS: si potrebbe fare un ulteriore passo avanti e creare eventi separati, uno per ciascuna proprietà. Quindi ci saranno eventi come 'OnTeplota',' OnVlhkost', ecc. Che restituiscono il nuovo valore. –

1

Ho utilizzato due timer per aggiornare "lentamente" alcuni valori (in modo che l'utente possa vedere l'avanzamento) senza rallentare l'applicazione o, nel peggiore dei casi, il sistema.

// "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;