2010-07-22 3 views
18

J'ai un TTimer dans mon application qui se déclenche toutes les 2 secondes et appelle mon gestionnaire d'événements, HandleTimerEvent(). La fonction HandleTimerEvent() modifie les ressources partagées et peut prendre 10 secondes à exécuter avant de retourner. En outre, j'appelle Sleep() dans le gestionnaire d'événements pour abandonner le processeur à certains moments. Je ne suis pas sûr comment l'objet TTimer du générateur C++ fonctionne quand il s'agit d'appeler des événements, donc le scénario que je viens d'expliquer m'a fait penser, en particulier, si HandleTimerEvent() est appelé avant qu'un appel précédent soit retourné.Le gestionnaire d'événements TTimer.OnTimer est-il réentrant?

La question se résume à un certain nombre de choses.

L'objet TTimer met-il en file d'attente les événements?

L'objet TTimer peut-il appeler mon gestionnaire d'événements avant qu'un appel précédent ne soit renvoyé?

Répondre

31

Cette réponse suppose que TTimer est toujours implémenté pour utiliser les messages WM_Timer. Si la mise en œuvre a changé (depuis 2005), veuillez ne pas tenir compte.

Non, l'objet TTimer ne met pas en file d'attente les événements. Il est piloté par le message Windows WM_Timer et Windows ne laisse pas les messages WM_TIMER s'empiler dans la file d'attente de messages. Si l'intervalle de temporisateur suivant se produit et que Windows voit qu'un message WM_Timer se trouve déjà dans la file d'attente des messages de l'application, il n'ajoute aucun autre message WM_Timer à la file d'attente. (Idem pour WM_Paint, btw)

Oui, il est possible qu'un événement TTimer.OnTimer soit déclenché même si un gestionnaire d'événements précédent est toujours en cours d'exécution. Si vous faites quelque chose dans votre gestionnaire d'événements qui permet à l'application de traiter les messages, alors votre événement timer peut être réentré. Il est évident que votre gestionnaire d'événements appelle Application.ProcessMessages, mais il peut être beaucoup plus subtil que cela. Si quelque chose que vous appelez dans votre gestionnaire d'événements appelle en interne Application.ProcessMessages, ou appelle PeekMessage/GetMessage + DispatchMessage, ou ouvre une boîte de dialogue modale ou appelle une interface COM liée à un objet COM hors processus, les messages de la file d'attente des messages de votre application seront traités et pourront inclure votre prochain message WM_Timer.

Une solution simple consiste à désactiver l'objet de temporisateur lorsque vous entrez dans votre gestionnaire d'événements de temporisateur et de le réactiver lorsque vous quittez le gestionnaire d'événements de temporisateur. Cela empêchera les messages du minuteur de se déclencher pendant que votre gestionnaire d'événements fonctionne encore, quelles que soient les caractéristiques de gestion du message de votre code.

+10

+1 pour désactiver le temporisateur. Pour démontrer l'efficacité de la désactivation de la minuterie (ou une démonstration facile de ce qui peut mal tourner si vous ne le faites pas), affichez une boîte de message dans le gestionnaire de minuterie. Si vous ne désactivez pas la minuterie à l'entrée, les boîtes de message s'empilent. –

+2

Vous pouvez également utiliser un indicateur booléen pour empêcher la réentrance dans le gestionnaire d'événements du temporisateur, mais la désactivation du temporisateur lui-même est beaucoup plus simple. – dthorpe

+0

Voir https://forums.embarcadero.com/thread.jspa?messageID=171751𩻧 pour une classe TTimerGuard utile, une classe de style RAII pour l'utilisation de TTimer. Peut nécessiter d'ajuster l'utilisation de FInterval en fonction de votre implémentation. –

2

J'utilise largement TTimer. Il ne met pas les événements en file d'attente. Si vous voulez le transférer à un gestionnaire d'événements, créez un TThread qui gère vos événements afin que le Timer puisse continuer son travail. La minuterie ne fonctionne pas de manière asynchrone, mais plutôt de manière synchrone.

Questions connexes