Comment puis-je réduire les tâches lorsque traitement des messages dans une boucle longue

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

  •  25-09-2019
  •  | 
  •  

Question

J'ai quelques longues boucles simples mais dans mon programme Delphi qui peut boucler des millions de fois et de prendre quelques secondes pour exécuter. Le code à l'intérieur de la boucle est très rapide et a été optimisé. Il faut juste le temps parce qu'il est fait tant de fois.

par exemple:.

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

Maintenant, je ne veux pas que mon programme soit non réactif, donc je veux ajouter un Application.ProcessMessages dans la boucle. Mais je veux aussi les déclarations ajoutées à ralentir ma boucle aussi peu que possible.

Je suivais une liste chaînée, donc je n'ai même pas une variable de comptage disponible et il faudrait ajouter un si je voulais intervalles. Ou je dois ajouter une minuterie, mais il faut minimiser le temps de vérifier.

Comment dois-je mettre en œuvre ce pour minimiser les frais généraux qui est ajouté?


Conclusion:

Pour l'instant, je fais quelque chose comme la réponse de APZ28.

Mais il semble que long terme je devrais mettre en œuvre une sorte de filetage pour gérer cela. Merci pour l'avoir signalé, parce que je pensais que Application.ProcessMessages était la seule façon de le faire.

Était-ce utile?

La solution

Pouvez-vous mettre la boucle de travail dans un fil, libérant ainsi le fil conducteur pour le traitement de la boucle GUI.

Autres conseils

Mettre sous fil n'est pas trival car il nécessite blocage part des ressources le cas échéant. Un bon truc est d'avoir un compteur, après le traitement de la boucle #, appelez 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;

Je voudrais aussi voter pour un fil ou quelque chose comme Anreas' AsyncCalls . Pour interdire à l'utilisateur de faire des actions non autorisés pendant le temps nécessaire, vous pouvez définir un indicateur lorsque la routine commence et réinitialiser quand il se termine (vous devez mettre à jour Screen.Cursor de toute façon). Le fil conducteur peut vérifier ce drapeau et désactiver toutes les actions concernées dans leur cas OnUpdate.

La meilleure option est de déplacer la boucle dans son propre thread de travail de sorte que le thread principal ne soit pas bloqué, vous n'avez pas besoin d'appeler ProcessMessages () du tout.

Toutefois, si vous devez faire la boucle dans le thread principal, vous pouvez utiliser MsgWaitForMultipleObject () pour détecter quand appeler ProcessMessages (), à savoir:

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; 

En variante avec 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; 

En variante avec 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; 

Une question de décider est de savoir si oui ou non votre application peut continuer avant d'avoir la réponse à la quelle que soit la boucle calcule. Si elle ne peut pas, alors il n'y a pas beaucoup dans l'application étant « sensibles ». Si vous essayez de mettre à jour une barre de progression ou quelque chose, vous pouvez appeler .Repaint sur le contrôle contenant la barre de progression chaque certain nombre d'itérations pour que la barre de progression repeindre.

Si l'application peut continuer, au moins pendant un certain temps, puis mettre le code dans un thread est une bonne idée.

Mettre le code en boucle dans un thread est probablement raisonnable de toute façon, surtout si vous voulez faire des choses comme peut-être abandonner le traitement. Si vous ne l'avez jamais utilisé avant les discussions, il y a un peu d'une courbe d'apprentissage, mais pour une simple boucle comme vous décrire il y a beaucoup d'exemples sur le web.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top