Multi threaded minuteries
Jamais ajouté un TTimer à votre demande seulement pour trouver que son cas n'est pas exécuté parce que le principal VCL thread est occupé?
Développeur Delphi | septembre 2000 |
le droit d'Auteur Pinnacle Publishing, Inc. Tous droits réservés.
le Vrai Minuterie Veuillez Exécuter?Steve Zimmelmanla Nécessité est la mère de l'invention, et cette fois ne fait pas exception. Alors que je travaillais sur certains Microsoft Word/Excel intégration, un problème se pose lorsque j'ai été le traitement des documents avec la propriété Visible à False. Word affiche une boîte de dialogue (que l'utilisateur ne pouvait pas voir) et attendre une réponse. Eh bien, inutile de dire que cela a fait la demande apparaît comme si elle était accrochée. J'ai besoin d'un moyen à la fois le processus et de gérer les choses, doivent-ils prendre trop de temps. Donc j'ai giflé un TTimer composant sur le formulaire, définissez l'intervalle de 1 000 habitants (une seconde), et a compté le nombre de fois où l'événement timer exécuté. Ça sonne bien, non? Faux! Lorsque le Mot de la boîte de dialogue a montré lui-même, il a été l'utilisation de la principale VCL fil, qui à son tour n'était pas la façon de donner à tous les autres processus, y compris TTimer. Donc, une fois de plus, l'application est apparu comme s'il s'était pendu.
La solution a été de créer un thread qui pourrait suivre le temps, peu importe comment occupé le principal VCL fil a été. Mais la création d'un thread à chaque fois que j'avais besoin de cette fonctionnalité ne semble pas être la meilleure solution à partir d'un objet-orienté point de vue. J'ai donc créé un nouveau composant Timer qui permettrait de créer et d'utiliser sa propre filetage interne, et exécuter une méthode de événement à un intervalle spécifique.
Le TThreadTimer composant est le résultat. C'est un simple sous-classe de TComponent qui gère un filetage interne. Le thread est créé et détruit par le composant. Lorsque la propriété Enabled est Vrai, le thread est créé, et si la valeur est False, il est détruit. Je voulais aussi assurez-vous que le fil n'a pas créées en mode création. Donc j'ai vérifié le ComponentState avant d'exécuter l'une de ces méthodes:
Procédure TThreadTimer.SetEnabled(Value:Boolean)
Begin
& nbsp & nbsp & nbsp if (Valeur <> FEnabled), Puis Commencer
& ! & ! & ! & ! & ! & nbsp FEnabled := Valeur
& ! & ! & ! & ! & ! & nbsp // Ne pas Créer ou de Tuer le thread, à moins
& ! & ! & ! & ! & ! & nbsp // l'application est en cours d'exécution.
& ! & ! & ! & ! & ! & nbsp Si ce N' (csDesigning Dans ComponentState)
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Puis Commencer
& ! & ! & ! & ! & nbsp & ! & ! & ! & nbsp Si FEnabled Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FTimerEventCount := 0
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread := TTheThread.Create(Self)
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp End Else Begin
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp KillThread
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Fin
Fin
j'ai utilisé une méthode appelée KillThread pour arrêter le fil de l'exécution et de la gratuit. Avant que je puisse réellement détruire le thread, j'ai besoin d'être sûr que l'événement associé n'est pas dans le milieu de traitement. Si le thread a été libéré alors que la manifestation était en cours d'exécution, une exception peut être soulevée parce que l'événement serait de retour à un thread qui n'existait plus. J'ai manipulé avec une simple variable Booléenne, FOnTimerEventInProgress, qui a initialisé à True avant l'événement timer a été exécuté, et sont passés à Faux juste après avoir terminé:
Procédure TThreadTimer.KillThread
Begin
& nbsp & nbsp & nbsp Essayer
& ! & ! & ! & ! & ! & nbsp FEnabled := False
& ! & ! & ! & ! & ! & nbsp Si (FThread <> Nil) Ensuite, Commencez
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp // Attendre la OnTimerInterval Événement
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp // terminer avant de mettre fin à la discussion.
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Tandis que FThread.FOnTimerEventInProgress
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Do Begin
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread.Résilier
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread.Gratuit
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Enfin
& ! & ! & ! & ! & ! & nbsp FThread := Nil
& ! & ! & ! & ! & ! & nbsp FTimerEventCount := 0
& nbsp & nbsp & nbsp Fin
Fin
Parce que le composant comprend un fil d'objet, et l'objet thread doit renvoyer à une partie de la composante des propriétés et des méthodes, j'ai utilisé le Créer, le Constructeur de la thread pour capturer une référence à son propriétaire:
Constructeur TTheThread.Create(AOwner:TThreadTimer)
Begin
& nbsp & nbsp & nbsp FOnTimerEventInProgress := False
& nbsp & nbsp & nbsp // Nous avons besoin d'accéder à certaines de du Propriétaire
& nbsp & nbsp & nbsp // propriétés de la thread.
& nbsp & nbsp & nbsp FOwner := AOwner
& nbsp & nbsp & nbsp Hérité de Créer(False)
Fin
Le thread de l'objet en lui même est plutôt simple. Il a Créer un Constructeur,de l'Exécuter, et une méthode OnTimer. La méthode Execute exécute juste après le Créer Constructeur, et reste actif jusqu'à ce que la méthode Execute sorties. J'ai utilisé la Résiliation de la propriété pour garder le fil vivant jusqu'à ce qu'il dit de manière explicite à la sortie. Mis fin a la valeur True lorsque le fil de Résilier la méthode est appelée. Dans l'exécution de la boucle, j'utilise un simple Sleep() pour mettre en pause le thread et d'attendre pour l'intervalle de minuterie. J'ai utiliser l'Application.ProcessMessages pour être sûr que le thread a tous les courants de traitement de l'information avant l'exécution de l'événement timer. J'ai inclus une propriété dans le principal composant appelé SynchronizeEvent. Cela rend le TThreadTimer se comporter comme un standard TTimer composant, par la synchronisation de l'événement avec les principaux VCL thread:
Procédure TTheThread.Exécuter
Begin
& nbsp & nbsp & nbsp Alors que Pas de Soi.Résilié Do Begin
& ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & nbsp Sommeil(FOwner.Intervalle)
& ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & nbsp Si FOwner.SynchronizeEvent Puis
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Synchroniser(OnTimer)
& ! & ! & ! & ! & ! & nbsp Else
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp OnTimer
& nbsp & nbsp & nbsp Fin
Fin
Procédure TTheThread.OnTimer
Begin
& nbsp & nbsp & nbsp Essayer
& ! & ! & ! & ! & ! & nbsp Si Affecté(FOwner.FOnTimerInterval), Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Si FOwner.Activée, Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FOnTimerEventInProgress := True
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Inc(FOwner.FTimerEventCount)
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FOwner.FOnTimerInterval(FOwner)
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Enfin
& ! & ! & ! & ! & ! & nbsp FOnTimerEventInProgress := False
& nbsp & nbsp & nbsp Fin
Fin
Il y a trois éléments essentiels à la TThreadTimer composant: le Fil, de l'Intervalle, et la TimerEvent. Comme mentionné précédemment, le thread est créé et détruit par la propriété Enabled. Dès que le thread est créé, la minuterie est active et exécute le OnTimerInterval événement au terme de la période de l'intervalle écoulé.
Il y a également une fonction appelée TimerEventCount-il fait exactement ce que son nom implique. Il incrémente un entier à chaque fois que le OnTimerInterval événement est traité. Ceci peut être utilisé à regarder pour les éventuels délais d'attente. Par exemple, vous pourriez avoir l'intervalle défini pour 1 000 (une seconde), et le cas du piège tendu TimerEventCount >= 60. Donc, si l'événement s'exécute 60 fois, puis une minute s'est écoulée et vous pourriez avoir besoin de manipuler quelque chose dans l'application. Mais rappelez-vous, ce minuteur est sur un thread séparé, afin de tenter de manipuler d'autres objets VCL dans le thread principal résultat dans les exceptions.
Après la création de la ThreadTimer objet, il n'a pas fallu longtemps pour réaliser que le compte à rebours de l'événement serait impossible de mettre à jour toute la VCL objets tandis que le thread principal était occupé. Par exemple, si vous vouliez vérifier un pointeur d'enregistrement d'une table pendant que la table est en cours de traitement et d'afficher les résultats pour un TLabel, vous serez hors de la chance. TLabel attendre jusqu'à ce que le thread principal permis de mettre à jour. Donc, retour à la proverbiale planche à dessin une fois de plus.
j'ai découvert que TCanvas, pour la plupart, n'utilisez pas le thread principal pour la mise à jour. J'ai donc créé un nouveau label composant appelé TCanvasLabel. TCanvasLabel est une sous-classe de TGraphicControl, et n'utilise que la Toile bien pour le dessin.
j'ai conçu TCanvasLabel de sorte qu'il pourrait être utilisé comme une Étiquette et une ProgressBar. Les propriétés de Remplissage, FillPercent, et FillColor sont utilisées pour peindre une partie de l'étiquette sur la toile d'une couleur différente. La méthode de Peinture est utilisé pour afficher le contenu de l'étiquette:
Procédure TCanvasLabel.Peinture
Var
& nbsp & nbsp & nbsp Rect : TRect
& nbsp & nbsp & nbsp fLeft,fhaut : Entier
Begin
& nbsp & nbsp & nbsp // redimensionnement automatique doit être définie à False lorsque l'étiquette
& nbsp & nbsp & nbsp // est utilisé dans un thread autre que le principal
& nbsp & nbsp & nbsp // VCL thread.
& nbsp & nbsp & nbsp Si AutoSize Puis
& ! & ! & ! & ! & ! & nbsp SetSize
& nbsp & nbsp & nbsp Rect := ClientRect
& nbsp & nbsp & nbsp // Peinture à l'étiquette principale couleur de fond.
& nbsp & nbsp & nbsp Toile.La brosse.Style := bsSolid
& nbsp & nbsp & nbsp Toile.La brosse.Couleur := Self.De couleur
& nbsp & nbsp & nbsp Toile.FillRect(ClientRect)
& nbsp & nbsp & nbsp Si (FillPercent > 0) Et FFill Puis Commencer
& ! & ! & ! & ! & ! & nbsp // calculer le pourcentage de remplissage.
& ! & ! & ! & ! & ! & nbsp Rect.Droit := Trunc((Rect.Droit *
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp (FillPercent / 100)))
& ! & ! & ! & ! & ! & nbsp Toile.La brosse.Couleur := FillColor
& ! & ! & ! & ! & ! & nbsp Toile.FillRect(Rect)
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Toile.La brosse.Style := bsClear
& nbsp & nbsp & nbsp Cas de l'Alignement De la
& ! & ! & ! & ! & ! & nbsp taLeftJustify:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := 0
& ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp taRightJustify:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := ClientRect.Droite
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextWidth(Légende)
& ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp taCenter:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := Trunc((ClientRect.Droite
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextWidth(Légende))/2)
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Cas de Mise en page De
& ! & ! & ! & ! & ! & nbsp tlTop: fhaut := 0
& ! & ! & ! & ! & ! & nbsp tlCenter:
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fhaut := Trunc((ClientRect.Bas -
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextHeight(Légende))/2)
& ! & ! & ! & ! & ! & nbsp tlBottom:
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fhaut := (Self.En hauteur
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextHeight(Légende))
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Toile.TextOut(fleft,fhaut,Légende)
& nbsp & nbsp & nbsp // force le canevas de mise à jour de lui-même.
& nbsp & nbsp & nbsp Toile.Actualiser
Fin
Il ya un couple de mises en garde vous avez besoin pour regarder dehors pour lorsque vous utilisez les événements qui utilisent des threads:
- La propriété AutoSize de TCanvasLabel doit être définie sur False lors de l'exécution de l'application. Le redimensionnement de l'étiquette utilise le principal VCL fil, de sorte que l'étiquette ne sera pas mise à jour.
- assurez-vous de ne pas inclure une norme de CLASSIFICATION des objets dans votre fil d'événements sans vraiment les tester, ou, plus spécifiquement, de les créer dans le code de l'intérieur de la thread.
pour Une utilisation simple de ces deux composants peuvent être trouvés dans la démo incluse dans l'accompagnement de fichier. La simple application démontre
la différence entre une norme TTimer et la minuterie trouvé dans les TThreadTimer composant.
Le TTimer mises à jour d'événements un TLabel à l'heure actuelle, une fois chaque seconde. Pour créer un chargé de processus, l'application ajoute 10 000 enregistrements dans une boucle For.
Vous remarquerez que lors de l'ajout de processus, l'affichage de l'heure s'arrête pour la norme TTimer tandis que les autres étiquettes sont mises à jour à leurs intervalles désignés.
& nbsp
Multi threaded minuteries
Multi threaded minuteries : Plusieurs milliers de conseils pour vous faciliter la vie.
Jamais ajoute un TTimer a votre demande seulement pour trouver que son cas n'est pas execute parce que le principal VCL thread est occupe?
Developpeur Delphi | septembre 2000 |
le droit d'Auteur Pinnacle Publishing, Inc. Tous droits reserves.
le Vrai Minuterie Veuillez Executer?Steve Zimmelmanla Necessite est la mere de l'invention, et cette fois ne fait pas exception. Alors que je travaillais sur certains Microsoft Word/Excel integration, un probleme se pose lorsque j'ai ete le traitement des documents avec la propriete Visible a False. Word affiche une boîte de dialogue (que l'utilisateur ne pouvait pas voir) et attendre une reponse. Eh bien, inutile de dire que cela a fait la demande apparaît comme si elle etait accrochee. J'ai besoin d'un moyen a la fois le processus et de gerer les choses, doivent-ils prendre trop de temps. Donc j'ai gifle un TTimer composant sur le formulaire, definissez l'intervalle de 1 000 habitants (une seconde), et a compte le nombre de fois ou l'evenement timer execute. Ça sonne bien, non? Faux! Lorsque le Mot de la boîte de dialogue a montre lui-meme, il a ete l'utilisation de la principale VCL fil, qui a son tour n'etait pas la façon de donner a tous les autres processus, y compris TTimer. Donc, une fois de plus, l'application est apparu comme s'il s'etait pendu.
La solution a ete de creer un thread qui pourrait suivre le temps, peu importe comment occupe le principal VCL fil a ete. Mais la creation d'un thread a chaque fois que j'avais besoin de cette fonctionnalite ne semble pas etre la meilleure solution a partir d'un objet-oriente point de vue. J'ai donc cree un nouveau composant Timer qui permettrait de creer et d'utiliser sa propre filetage interne, et executer une methode de evenement a un intervalle specifique.
Le TThreadTimer composant est le resultat. C'est un simple sous-classe de TComponent qui gere un filetage interne. Le thread est cree et detruit par le composant. Lorsque la propriete Enabled est Vrai, le thread est cree, et si la valeur est False, il est detruit. Je voulais aussi assurez-vous que le fil n'a pas creees en mode creation. Donc j'ai verifie le ComponentState avant d'executer l'une de ces methodes:
Procedure TThreadTimer.SetEnabled(Value:Boolean)
Begin
& nbsp & nbsp & nbsp if (Valeur <> FEnabled), Puis Commencer
& ! & ! & ! & ! & ! & nbsp FEnabled := Valeur
& ! & ! & ! & ! & ! & nbsp // Ne pas Creer ou de Tuer le thread, a moins
& ! & ! & ! & ! & ! & nbsp // l'application est en cours d'execution.
& ! & ! & ! & ! & ! & nbsp Si ce N' (csDesigning Dans ComponentState)
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Puis Commencer
& ! & ! & ! & ! & nbsp & ! & ! & ! & nbsp Si FEnabled Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FTimerEventCount := 0
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread := TTheThread.Create(Self)
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp End Else Begin
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp KillThread
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Fin
Fin
j'ai utilise une methode appelee KillThread pour arreter le fil de l'execution et de la gratuit. Avant que je puisse reellement detruire le thread, j'ai besoin d'etre sûr que l'evenement associe n'est pas dans le milieu de traitement. Si le thread a ete libere alors que la manifestation etait en cours d'execution, une exception peut etre soulevee parce que l'evenement serait de retour a un thread qui n'existait plus. J'ai manipule avec une simple variable Booleenne, FOnTimerEventInProgress, qui a initialise a True avant l'evenement timer a ete execute, et sont passes a Faux juste apres avoir termine:
Procedure TThreadTimer.KillThread
Begin
& nbsp & nbsp & nbsp Essayer
& ! & ! & ! & ! & ! & nbsp FEnabled := False
& ! & ! & ! & ! & ! & nbsp Si (FThread <> Nil) Ensuite, Commencez
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp // Attendre la OnTimerInterval Evenement
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp // terminer avant de mettre fin a la discussion.
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Tandis que FThread.FOnTimerEventInProgress
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Do Begin
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread.Resilier
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp FThread.Gratuit
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Enfin
& ! & ! & ! & ! & ! & nbsp FThread := Nil
& ! & ! & ! & ! & ! & nbsp FTimerEventCount := 0
& nbsp & nbsp & nbsp Fin
Fin
Parce que le composant comprend un fil d'objet, et l'objet thread doit renvoyer a une partie de la composante des proprietes et des methodes, j'ai utilise le Creer, le Constructeur de la thread pour capturer une reference a son proprietaire:
Constructeur TTheThread.Create(AOwner:TThreadTimer)
Begin
& nbsp & nbsp & nbsp FOnTimerEventInProgress := False
& nbsp & nbsp & nbsp // Nous avons besoin d'acceder a certaines de du Proprietaire
& nbsp & nbsp & nbsp // proprietes de la thread.
& nbsp & nbsp & nbsp FOwner := AOwner
& nbsp & nbsp & nbsp Herite de Creer(False)
Fin
Le thread de l'objet en lui meme est plutot simple. Il a Creer un Constructeur,de l'Executer, et une methode OnTimer. La methode Execute execute juste apres le Creer Constructeur, et reste actif jusqu'a ce que la methode Execute sorties. J'ai utilise la Resiliation de la propriete pour garder le fil vivant jusqu'a ce qu'il dit de maniere explicite a la sortie. Mis fin a la valeur True lorsque le fil de Resilier la methode est appelee. Dans l'execution de la boucle, j'utilise un simple Sleep() pour mettre en pause le thread et d'attendre pour l'intervalle de minuterie. J'ai utiliser l'Application.ProcessMessages pour etre sûr que le thread a tous les courants de traitement de l'information avant l'execution de l'evenement timer. J'ai inclus une propriete dans le principal composant appele SynchronizeEvent. Cela rend le TThreadTimer se comporter comme un standard TTimer composant, par la synchronisation de l'evenement avec les principaux VCL thread:
Procedure TTheThread.Executer
Begin
& nbsp & nbsp & nbsp Alors que Pas de Soi.Resilie Do Begin
& ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & nbsp Sommeil(FOwner.Intervalle)
& ! & ! & ! & ! & ! & nbsp Application.ProcessMessages
& ! & ! & ! & ! & ! & nbsp Si FOwner.SynchronizeEvent Puis
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Synchroniser(OnTimer)
& ! & ! & ! & ! & ! & nbsp Else
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp OnTimer
& nbsp & nbsp & nbsp Fin
Fin
Procedure TTheThread.OnTimer
Begin
& nbsp & nbsp & nbsp Essayer
& ! & ! & ! & ! & ! & nbsp Si Affecte(FOwner.FOnTimerInterval), Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Si FOwner.Activee, Puis Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FOnTimerEventInProgress := True
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Inc(FOwner.FTimerEventCount)
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp FOwner.FOnTimerInterval(FOwner)
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Enfin
& ! & ! & ! & ! & ! & nbsp FOnTimerEventInProgress := False
& nbsp & nbsp & nbsp Fin
Fin
Il y a trois elements essentiels a la TThreadTimer composant: le Fil, de l'Intervalle, et la TimerEvent. Comme mentionne precedemment, le thread est cree et detruit par la propriete Enabled. Des que le thread est cree, la minuterie est active et execute le OnTimerInterval evenement au terme de la periode de l'intervalle ecoule.
Il y a egalement une fonction appelee TimerEventCount-il fait exactement ce que son nom implique. Il incremente un entier a chaque fois que le OnTimerInterval evenement est traite. Ceci peut etre utilise a regarder pour les eventuels delais d'attente. Par exemple, vous pourriez avoir l'intervalle defini pour 1 000 (une seconde), et le cas du piege tendu TimerEventCount >= 60. Donc, si l'evenement s'execute 60 fois, puis une minute s'est ecoulee et vous pourriez avoir besoin de manipuler quelque chose dans l'application. Mais rappelez-vous, ce minuteur est sur un thread separe, afin de tenter de manipuler d'autres objets VCL dans le thread principal resultat dans les exceptions.
Apres la creation de la ThreadTimer objet, il n'a pas fallu longtemps pour realiser que le compte a rebours de l'evenement serait impossible de mettre a jour toute la VCL objets tandis que le thread principal etait occupe. Par exemple, si vous vouliez verifier un pointeur d'enregistrement d'une table pendant que la table est en cours de traitement et d'afficher les resultats pour un TLabel, vous serez hors de la chance. TLabel attendre jusqu'a ce que le thread principal permis de mettre a jour. Donc, retour a la proverbiale planche a dessin une fois de plus.
j'ai decouvert que TCanvas, pour la plupart, n'utilisez pas le thread principal pour la mise a jour. J'ai donc cree un nouveau label composant appele TCanvasLabel. TCanvasLabel est une sous-classe de TGraphicControl, et n'utilise que la Toile bien pour le dessin.
j'ai conçu TCanvasLabel de sorte qu'il pourrait etre utilise comme une Etiquette et une ProgressBar. Les proprietes de Remplissage, FillPercent, et FillColor sont utilisees pour peindre une partie de l'etiquette sur la toile d'une couleur differente. La methode de Peinture est utilise pour afficher le contenu de l'etiquette:
Procedure TCanvasLabel.Peinture
Var
& nbsp & nbsp & nbsp Rect : TRect
& nbsp & nbsp & nbsp fLeft,fhaut : Entier
Begin
& nbsp & nbsp & nbsp // redimensionnement automatique doit etre definie a False lorsque l'etiquette
& nbsp & nbsp & nbsp // est utilise dans un thread autre que le principal
& nbsp & nbsp & nbsp // VCL thread.
& nbsp & nbsp & nbsp Si AutoSize Puis
& ! & ! & ! & ! & ! & nbsp SetSize
& nbsp & nbsp & nbsp Rect := ClientRect
& nbsp & nbsp & nbsp // Peinture a l'etiquette principale couleur de fond.
& nbsp & nbsp & nbsp Toile.La brosse.Style := bsSolid
& nbsp & nbsp & nbsp Toile.La brosse.Couleur := Self.De couleur
& nbsp & nbsp & nbsp Toile.FillRect(ClientRect)
& nbsp & nbsp & nbsp Si (FillPercent > 0) Et FFill Puis Commencer
& ! & ! & ! & ! & ! & nbsp // calculer le pourcentage de remplissage.
& ! & ! & ! & ! & ! & nbsp Rect.Droit := Trunc((Rect.Droit *
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp (FillPercent / 100)))
& ! & ! & ! & ! & ! & nbsp Toile.La brosse.Couleur := FillColor
& ! & ! & ! & ! & ! & nbsp Toile.FillRect(Rect)
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Toile.La brosse.Style := bsClear
& nbsp & nbsp & nbsp Cas de l'Alignement De la
& ! & ! & ! & ! & ! & nbsp taLeftJustify:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := 0
& ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp taRightJustify:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := ClientRect.Droite
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextWidth(Legende)
& ! & ! & ! & ! & ! & nbsp Fin
& ! & ! & ! & ! & ! & nbsp taCenter:
& ! & ! & ! & ! & ! & nbsp Commencer
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fLeft := Trunc((ClientRect.Droite
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextWidth(Legende))/2)
& ! & ! & ! & ! & ! & nbsp Fin
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Cas de Mise en page De
& ! & ! & ! & ! & ! & nbsp tlTop: fhaut := 0
& ! & ! & ! & ! & ! & nbsp tlCenter:
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fhaut := Trunc((ClientRect.Bas -
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextHeight(Legende))/2)
& ! & ! & ! & ! & ! & nbsp tlBottom:
& ! & ! & ! & ! & ! & ! & ! & ! & nbsp fhaut := (Self.En hauteur
& ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & ! & nbsp Toile.TextHeight(Legende))
& nbsp & nbsp & nbsp Fin
& nbsp & nbsp & nbsp Toile.TextOut(fleft,fhaut,Legende)
& nbsp & nbsp & nbsp // force le canevas de mise a jour de lui-meme.
& nbsp & nbsp & nbsp Toile.Actualiser
Fin
Il ya un couple de mises en garde vous avez besoin pour regarder dehors pour lorsque vous utilisez les evenements qui utilisent des threads:
- La propriete AutoSize de TCanvasLabel doit etre definie sur False lors de l'execution de l'application. Le redimensionnement de l'etiquette utilise le principal VCL fil, de sorte que l'etiquette ne sera pas mise a jour.
- assurez-vous de ne pas inclure une norme de CLASSIFICATION des objets dans votre fil d'evenements sans vraiment les tester, ou, plus specifiquement, de les creer dans le code de l'interieur de la thread.
pour Une utilisation simple de ces deux composants peuvent etre trouves dans la demo incluse dans l'accompagnement de fichier. La simple application demontre
la difference entre une norme TTimer et la minuterie trouve dans les TThreadTimer composant.
Le TTimer mises a jour d'evenements un TLabel a l'heure actuelle, une fois chaque seconde. Pour creer un charge de processus, l'application ajoute 10 000 enregistrements dans une boucle For.
Vous remarquerez que lors de l'ajout de processus, l'affichage de l'heure s'arrete pour la norme TTimer tandis que les autres etiquettes sont mises a jour a leurs intervalles designes.
& nbsp
Multi threaded minuteries
By commentfaire
Multi threaded minuteries : Plusieurs milliers de conseils pour vous faciliter la vie.