Je ne comprends pas quelle Application.ProcessMessages dans Delphi fait [fermé]

Je suis un noobie à Delphi, désolé pour toute question stupide.

Mon superviseur m'a expliqué cette demande.ProcessMessages empêche le gel de l'application et alloue du temps de calcul supplémentaire. Mais dans les documents de cette commande est toujours quelque chose expliqué sur un système de file d'attente qui est traité? Svp quelqu'un peut-il m'expliquer le contexte?

23
demandé sur user3133542 2014-08-07 15:47:17

2 réponses

Il n'y a pas de moyen court de répondre correctement à cette question.

Le principal moyen par lequel les applications Windows interagissent avec le système d'exploitation est via un système de messagerie. Tout ce qui se passe dans une application windows se produit en réponse à un message.

Par exemple :

Si vous cliquez sur l'écran, le système d'exploitation décide quelle application a été cliquée et poste un message à cette application indiquant qu'il a reçu un clic (le long avec l'emplacement de la souris).

Si une fenêtre est déplacée et révèle une partie de votre application en dessous, le système d'exploitation envoie un message indiquant à votre application de se repeindre.

La liste continue. Tout ce qui se passe est conduit par des messages.

Maintenant, chaque application a un thread d'interface utilisateur principal (le thread" principal") et ce thread a une fonction principale - il s'exécute dans une boucle infinie qui vérifie ces messages du système d'exploitation et exécute ensuite le code nécessaire en réponse à ces messages.

Le Problème

Vous venez et commencez à écrire une demande. Vous pourriez écrire du code comme ceci:

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
  end;
end;

Dans la structure plus grande du programme, l'exécution dans le thread principal ressemble à ceci:

  1. Vérifier les messages
  2. pour chaque message - exécutez les gestionnaires associés
  3. retour pour vérifier les messages (boucle)

Donc cette boucle est heureusement wizzing le long quand soudainement l'un des gestionnaires associés (Button1Click ci-dessus) commence à prendre beaucoup de temps à terminer. La Clé à comprendre est qu'un gestionnaire de messages doit se terminer avant que le suivant puisse s'exécuter. si vous cliquez sur une barre de défilement, par exemple, et faites-la glisser, mais que vous avez attaché un gestionnaire à OnClick de la barre de défilement qui prend 10s pour terminer, l'opération de glisser ne sera pas vue par votre application jusqu'à ce que ce gestionnaire de clic soit terminé. En attendant, le message de la file d'attente se remplit et le fil principal ne fait rien à ce sujet.

Vous avez sûrement connu cela-soudainement votre application ne répond pas aux clics. Vous ne pouvez pas interagir avec elle, vous ne pouvez pas déplacer la fenêtre, si vous faites glisser une autre fenêtre par-dessus, l'application ne se repeindra même pas - elle se remplit de toutes les ordures que vous avez laissées sur elle.

Entrez ProcessMessages

la solution paresseuse et terrible pour ne pas mettre votre code de longue durée dans un thread

Ce que vous faites lorsque vous appelez Application.ProcessMessages, c'est, au milieu de l'un de ces gestionnaires, demander au thread principal de faire une pause pour revenir vérifier la file d'attente des messages et vider tous les messages qui s'accumulent; pour gérer les clics, les mouvements de fenêtre, les entrées, les frappes, se repeindre si nécessaire, etc.

procedure TForm1.Button1Click(Sender: TObject);
var i : Integer;
begin
  for i := 0 to 99999999999 do begin
    SolveTheProblemsOfTheWorld(i);
    CalculatePiToABillionPlaces;
    Application.ProcessMessages;
  end;
end;

Cela peut sembler superficiellement comme une chose raisonnable à faire car il vous permet de garder l'application sensible au cours d'une longue boucle. En fin de compte, cependant, ce style de programmation est largement considéré comme une pratique extrêmement médiocre pour un grand nombre de très bonnes raisons. Il suffit de dire que partout où vous êtes tenté d'utiliser Application.ProcessMessages est un cas solide pour déplacer ce travail vers un thread d'arrière-plan à la place.

Pour plus de détails, regardons le code réel:

procedure TApplication.ProcessMessages;
var
  Msg: TMsg;
begin
  while ProcessMessage(Msg) do {loop};
end;

Ainsi, lorsque vous effectuez cet appel à Application.ProcessMessages, vous exécutez une boucle qui est, une par une, vidant les messages du file d'attente des messages (et l'exécution de tout le code qui est attaché aux gestionnaires qui réagissent à ces messages) jusqu'à ce qu'il soit vide. Quand il est vide et qu'il n'y a plus de messages à traiter, le contrôle reviendra à la ligne suivante de votre programme.

Le point important à emporter est que l'interaction fluide et fluide que vous rencontrez lors de l'utilisation d'un programme est une illusion complète qui dépend exactement de cette boucle de message traitée le plus rapidement possible. Plus le délai est petit entre un message affiché sur votre application et le message traité, plus votre application se sentira vivante et réactive.

C'est pour cette raison que tout le code attaché aux gestionnaires de l'interface utilisateur doit fonctionner rapidement. Les opérations de longue durée doivent être interrompues pour que la gestion des messages puisse continuer (c'est-à-dire: Application.ProcessMessages) ou ces opérations doivent être déplacées vers un thread séparé où elles peuvent s'exécuter sans attacher le thread principal et prendre loin de sa responsabilité principale (qui est de garder l'interface utilisateur de son vivant).


Pour un très bon article sur le sujet, voir :

L'Odyssée D'une clé par Peter ci-dessous

(cache de Google lien... serveur semble occupé pour moi)

résumé: cet article suit le chemin d'un message de frappe à travers la VCL. Vous apprendrez comment le traitement des clés est implémenté, comment fonctionnent les événements OnKey et quels sont les points d'intervention le programmateur peut être trouvé dans l'ensemble du processus. En outre, des choses comme le traitement des messages sont expliquées, et vous apprendrez comment tracer les messages dans le débogueur de la boucle de message à leur destination éventuelle.

42
répondu J... 2014-08-07 13:55:33

Messagerie

Delphi est une application Windows et beaucoup de communication entre les composants Windows (comme les formulaires, les boîtes d'édition, mais aussi des choses cachées comme les minuteries) se fait en utilisant des choses appelées messages.

Ces messages sont envoyés à une file d'attente spéciale. Vous pouvez envoyer ces messages directement (en utilisant les fonctions API SendMessage et PostMessage), mais les messages sont également envoyés indirectement lorsque vous définissez une propriété, comme le Text d'un TEdit. Ces messages sont utilisés pour aviser les contrôles que des événements importants se sont produits dans le cadre du programme afin qu'ils puissent réagir de manière appropriée. Ce concept d'envoi et de réponse aux messages est le principe de base d'un paradigme connu sous le nom de programmation axée sur les événements.

De nombreux systèmes d'exploitation modernes sont axés sur les événements, tels que Windows. En fait, le Delphi VCL enveloppe beaucoup de fonctionnalités de Windows, et beaucoup de choses que vous faites se traduira par des messages envoyés entre les contrôles, qui notifient ces contrôles de la souris clics, pressions sur le clavier, Windows-modification des paramètres, fermeture de l'application, déplacement de contrôle, etc.

Mainthread ne traite pas les messages lors de l'exécution du code

Les systèmes d'exploitation permettent également aux programmes d'avoir des choses appelées threads. Les Threads sont comme de petits morceaux d'un programme qui semblent fonctionner simultanément (et dans certains cas s'exécutent simultanément). Une application peut avoir un ou plusieurs threads, mais il y a toujours le thread principal, qui est aussi le fil responsable de l'acceptation des messages entrants et de la mise à jour de L'interface graphique.

Lorsque le thread principal de l'application est inactif (n'exécutant pas de code), il vérifie la file d'attente pour voir s'il y a des messages et les traite. Alors que d'autres codes non liés sont en cours d'exécution, cela ne peut pas se produire. Considérons cet exemple:

Label1.Caption := 'A';
Sleep(5000); // Sleep is simulating a long process.
Label1.Caption := 'B';

Si vous exécutez ce code, vous vous attendez à ce que l'étiquette obtienne une légende de 'A', qui devient 'B' cinq secondes plus tard. Mais ce n'est pas vrai. Définition de la légende d'une étiquette déclenche une repeinte du contrôle via un message. Parce que le thread principal est toujours bloqué par ce code (même par la commande Sleep), le message n'est pas encore traité.

Seulement lorsque les 5 secondes se sont écoulées et que la légende est définie, le thread principal devient inactif et exécutera les repeints que nous avons déclenchés en définissant la légende. Notez également que d'autres interactions, comme cliquer sur un bouton ou faire glisser la fenêtre sont reportées jusqu'à ce que les 5 secondes se soient écoulées. Le principal ensemble thread se bloque.

Demande.ProcessMessages à la rescousse

Demande.ProcessMessages va simplement forcer l'application à vider sa file d'attente de messages, de sorte que vous pouvez "réparer" le code comme ceci:

Label1.Caption := 'A';
Application.ProcessMessages;
Sleep(5000);
Label1.Caption := 'B';

Habituellement, il n'y aura pas juste un sommeil dans votre code, mais beaucoup de traitement réel. En utilisant fréquemment Application.ProcessMessage, Vous pouvez garder l'interface de l'application en réponse, même lorsque vous exécutez du code.

Mais c'est un peu sale, le fil principal (et l'interface) sera toujours bloquée, sauf si vous parvenez à insérer un lot {[23] } d'appels à Application.ProcessMessages. Et en dehors du traitement des messages paint, d'autres messages sont également traités, de sorte que l'application peut traiter un événement de clic En plein milieu de votre processus.

Donc, les documents que vous lisez ont raison: Application.ProcessMessages videra la file d'attente de messages . Votre superviseur n'a pas tout à fait raison. Il n'alloue pas de temps de traitement supplémentaire par exemple, mais il intègre simplement la vidange de la file d'attente de messages dans le code qui est exécuté, alors que normalement les messages resteraient dans la file d'attente jusqu'à ce que l'application devienne inactive.

Internes

En Interne, l'application elle-même fait la même chose tout le temps. Sa procédure principale, Application.Run, ressemble à ceci (pseudo code simplifié):

  repeat

    ProcessMessageFromTheQueue;
    If QueueIsEmpty then
      TellWindowsToWakeMeUpWhenANewMessageArrives;   

  until Terminated;

C'est le traitement qui exécute le code, par exemple lorsqu'un message WM_MOUSECLICK est traité, il déclenchera (via une magie Delphi VCL), votre Button1Click gestionnaire d'événements. Comme il s'agit d'un seul thread, tout s'exécute séquentiellement, vous comprendrez donc que ProcessMessageFromTheQueue ne renvoie que lorsque le gestionnaire d'événements est terminé. Delphi ne gère qu'un seul message à la fois, et ne fait que le suivant lorsque le traitement du précédent est terminé.

Ou est-ce le cas?

Application.ProcessMessages on dirait ceci:

  repeat

    ProcessMessageFromTheQueue;

  until QueueIsEmpty;

Donc, il fait presque la même chose que la boucle principale de l'application: il sélectionne un message de la file d'attente et le traite. Donc, cela signifie, Vous pouvez réellement traitez les messages suivants pendant que vous êtes à l'intérieur du traitement du dernier. Une astuce pratique, mais il peut facilement rendre votre application désordonnée.

La bonne façon: Threads

La plupart des gens considèrent qu'il est préférable d'utiliser un thread séparé pour exécuter votre code et d'envoyer simplement des signaux au thread principal lorsqu'il doit mettre à jour une étiquette ou une barre de progression. De cette façon, le thread principal est inactif tout le temps et continuera à répondre aux clics de souris etc.

Le filetage est difficile pour un débutant, cependant. Certaines choses à garder à l'esprit:

  • un thread (autre que le thread principal) ne doit pas mettre directement à jour L'interface graphique (par exemple, un thread ne peut pas définir Label1.Légende. Il pourrait échouer.
  • Les Variables et autres ressources ne doivent pas être modifiées par plusieurs threads en même temps, vous devez donc prendre des précautions pour éviter cela (sections critiques).
  • vous devez (dans la plupart des cas) empêcher qu'un utilisateur s'exécute la même action qui est déjà en cours d'exécution.
  • dans de nombreux cas, vous devez trouver un moyen approprié d'interrompre un thread en cours d'exécution si un Utilisateur souhaite annuler le processus ou fermer l'application.

Quoi qu'il en soit, cette réponse ne devrait pas être un cours complet sur le threading, mais maintenant au moins vous savez à quoi sert Application.ProcessMessages. Notez également, il est considéré comme " mal " par beaucoup de gens, mais en tant que débutant, vous pouvez l'essayer de toute façon pour découvrir ses forces et ses faiblesses par vous-même. Si vous le faites, j'espère que cela l'information vous incitera au moins à passer aux discussions dès que vous serez suffisamment expérimenté pour ce prochain chapitre.

29
répondu GolezTrol 2015-10-19 06:32:05