Come posso ridurre al minimo il sovraccarico durante l'elaborazione dei messaggi in un ciclo lungo

StackOverflow https://stackoverflow.com/questions/3402824

  •  25-09-2019
  •  | 
  •  

Domanda

Nel mio programma Delphi sono presenti alcuni cicli lunghi ma semplici che potrebbero ripetersi milioni di volte e richiedere alcuni secondi per l'esecuzione.Il codice all'interno del ciclo è molto veloce ed è stato ottimizzato.Ci vuole solo molto tempo perché è stato fatto così tante volte.

per esempio.:

Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
  { do something simple with R.Value }
  R := R.NextRecord;
end;
Screen.Cursor := crDefault;

Ora non voglio che il mio programma non risponda, quindi voglio aggiungere un Application.ProcessMessages all'interno del ciclo.Ma voglio anche che le istruzioni aggiunte rallentino il meno possibile il mio ciclo.

Sto seguendo un elenco collegato, quindi non ho nemmeno una variabile di conteggio disponibile e dovrei aggiungerne una se volessi intervalli.Oppure dovrei aggiungere un timer, ma devo ridurre al minimo il controllo del tempo.

Come dovrei implementarlo per ridurre al minimo il sovraccarico aggiunto?


Conclusione:

Per ora, sto facendo qualcosa di simile alla risposta di APZ28.

Ma sembra che a lungo termine dovrei implementare una sorta di threading per gestire questo problema.Grazie per averlo sottolineato, perché pensavo che Application.ProcessMessages fosse l'unico modo per farlo.

È stato utile?

Soluzione

Si può mettere il ciclo di lavoro in un thread, liberando il thread principale per la lavorazione del ciclo GUI.

Altri suggerimenti

Metterlo sotto il thread non è banale poiché richiede la risorsa di condivisione del blocco, se presente.Un buon trucco è avere un contatore che, dopo aver elaborato # di loop, chiami ProcessMessages

var
  LoopCounter: Integer;

LoopCounter := 0;
R := FirstRecord;
while R <> nil do begin
  Inc(LoopCounter);
  if (LoopCounter >= ???) then
  begin
    LoopCounter := 0;
    Application.ProcessMessages;
  end;

  { do something simple with R.Value }
  R := R.NextRecord;
end;

Vorrei anche votare per un thread o qualcosa di simile Anreas' AsyncCalls . Per proibire l'utente a fare qualunque azione non consentita durante il tempo necessario, è possibile impostare un flag quando inizia la routine e annullarlo quando si finisce (è necessario aggiornare Screen.Cursor comunque). Il filo conduttore in grado di controllare questo flag e disabilitare tutte le azioni coinvolte nel loro evento OnUpdate.

L'opzione migliore è quella di spostare l'anello in un proprio thread di lavoro in modo che il thread principale non è bloccato, allora non c'è bisogno di ProcessMessages call () a tutti.

Tuttavia, se si deve fare il loop nel thread principale, quindi è possibile utilizzare MsgWaitForMultipleObject () per rilevare quando chiamare ProcessMessages (), vale a dire:

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if MsgWaitForMultipleObjects(0, nil, False, 0, QS_ALLINPUT) = WAIT_OBJECT_0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

In alternativa con PeekMessage ():

var Msg: TMsg;

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

In alternativa con GetQueueStatus ():

Screen.Cursor = crHourGlass; 
R := FirstRecord; 
while R <> nil do begin 
  { do something simple with R.Value } 
  if GetQueueStatus(QS_ALLINPUT) <> 0 then
    Application.ProcessMessages;
  R := R.NextRecord; 
end; 
Screen.Cursor := crDefault; 

Una domanda da decidere è se la vostra applicazione può continuare prima di avere la risposta alla qualunque sia il ciclo è il calcolo. Se non è possibile, allora non ha più molto senso nell'applicazione essere "sensibile". Se si sta tentando di aggiornare una barra di avanzamento o qualcosa, è possibile chiamare .Repaint sul controllo che contiene la barra di avanzamento ogni certo numero di iterazioni per causare la barra di avanzamento per ridipingere.

Se l'applicazione può continuare, almeno per un po ', poi mettere il codice in un thread è una buona idea.

Mettere il codice di loop in un thread è probabilmente ragionevole in ogni caso, soprattutto se si vuole fare le cose come, eventualmente, di interruzione del trattamento. Se non avete mai usato le discussioni prima, c'è un po 'di una curva di apprendimento, ma per un semplice ciclo come te descrivere ci sono un sacco di esempi sul web.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top