SetTimer () pièges

J’ai un minuteur sans fenêtre (pas de WM_TIMER) qui déclenche une fonction de rappel une seule fois lorsqu’une période donnée est écasting. Il est implémenté en tant que SetTimer()/KillTimer() . Les périodes sont assez petites: 100-300 millisecondes.

Est-ce assez bon marché (je veux dire la performance) pour appeler la SetTimer()/KillTimer() à chaque intervalle court?

Que se passe-t-il si j’ai 100 temporisateurs de ce type qui appellent périodiquement SetTimer()/KillTimer() ? Combien d’objects du minuteur de fenêtre peuvent exister simultanément dans le système?

Telle est la question: utiliser un tas d’objects timer et compter sur une bonne implémentation Windows des timers, ou créer un object timer Windows qui coche tous les, disons, 30 millisecondes, et y abonner tous les timers uniques de 100-300 millisecondes personnalisés .

Merci

Le problème avec les messages de timer que vous essayez de les utiliser est qu’il s’agit de messages de faible priorité. En fait, ce sont de faux messages. Les temporisateurs sont associés à un object timer du kernel sous-jacent – lorsque la boucle de messages détecte ce feu, il marque simplement la queue des messages threads en cours avec un indicateur indiquant que le prochain appel à GetMessage – message juste à temps et le retourner.

Avec potentiellement beaucoup d’objects timer, il n’est pas du tout évident que le système signalera équitablement les messages de temporisation pour tous les temporisateurs, et toute charge système peut entièrement empêcher la génération de messages WM_TIMER pendant de longues périodes.

Si vous contrôlez la boucle de message, vous pouvez utiliser la gestion de votre propre liste d’événements de timer (avec les horodatages GetTickCount lorsqu’ils doivent se produire) et MSGWaitForMultipleObject – au lieu de GetMessage pour attendre les messages. Utilisez le paramètre dwTimeout pour fournir le plus petit intervalle – à partir de maintenant – jusqu’à ce que le prochain temporisateur soit signalé. Donc, il reviendra d’attendre des messages chaque fois que vous aurez une timer à traiter.

Et / ou vous pouvez utiliser des temporisateurs d’attente – soit sur un thread d’interface graphique avec MSGWaitForMultipleObjects, soit sur un thread de travail, pour accéder directement à la fonctionnalité de temporisation de niveau inférieur.

Le plus gros SetTimer() est qu’en fait, il s’agit d’un object USER (en dépit du fait qu’il ne figure pas dans la liste MSDN USER ), il est donc limité aux objects USER de Windows – par défaut 10000 objects maximum par processus processus en cours).

Cela peut être facilement prouvé par un simple test – appelez simplement SetTimer() (les parameters ne sont pas importants, les fenêtres et les fenêtres agissent de la même manière) et le nombre d’objects USER augmenté dans le Gestionnaire des tâches.

Voir aussi la source de ReactOS ntuser.h et cet article . Les deux déclarent que TYPE_TIMER est l’un des types de TYPE_TIMER USER.

Attention donc, créer un tas de timers pourrait épuiser les ressources de votre système et rendre votre processus en panne ou même tout le système insensible.

Voici les détails que je pense que vous êtes réellement après en posant cette question:

SetTimer () va d’abord parsingr la liste des temporisations non-kernel (liste à double lien) pour voir si l’ID du minuteur existe déjà. Si le minuteur existe, il sera simplement réinitialisé. Si ce n’est pas le cas, un appel HMAllocObject se produit et crée un espace pour la structure. La structure du temporisateur sera alors renseignée et liée à la tête de la liste.

Ce sera la surcharge totale pour la création de chacun de vos 100 temporisateurs. C’est exactement ce que fait la routine, sauf pour vérifier les parameters min et max dwElapsed.

En ce qui concerne l’expiration du temporisateur, la liste de temporisation est analysée à peu près (environ) la durée de la plus petite durée de timer observée lors de la dernière parsing de la liste de temporisation. (En fait, ce qui se passe réellement – un minuteur du kernel est défini sur la durée de la plus petite horloge utilisateur trouvée, et ce minuteur réveille le thread qui vérifie les expirations de la timer utilisateur et réveille les threads respectifs en définissant un indicateur leur statut de queue de messages.)

Pour chaque temporisateur de la liste, le delta actuel entre la dernière heure (en ms) de la liste de temporisation et l’heure actuelle (en ms) est décrémentée par rapport à chaque temporisateur de la liste. Quand un est dû (<= 0 restant), il est marqué comme "prêt" dans sa propre structure et un pointeur sur les informations de thread est lu depuis la structure du temporisateur et utilisé pour réveiller le thread respectif en définissant le drapeau QS_TIMER du thread. Il incrémente ensuite le compteur CurrentTimersReady de votre file d'attente de messages. C'est tout ce que fait l'expiration de la minuterie. Aucun message réel n'est affiché.

Lorsque votre pompe de messages principale appelle GetMessage (), alors qu’aucun autre message n’est disponible, GetMessage () vérifie la présence de QS_TIMER dans les bits de réveil de votre thread et, s’il est défini, génère un message WM_TIMER la liste marquée READY et associée à votre identifiant de thread. Il décrémente ensuite le compte CurrentTimersReady du thread et, si 0, efface le bit de réveil du temporisateur. Votre prochain appel à GetMessage () provoquera la même chose jusqu’à ce que tous les temporisateurs soient épuisés.

Les timers à un coup restnt instanciés. À leur expiration, ils sont marqués WAITING. L’appel suivant à SetTimer () avec le même ID de timer mettra simplement à jour et réactivera l’original. Les timers ponctuelles et périodiques se réinitialisent et ne meurent qu’avec KillTimer ou lorsque votre fil ou votre fenêtre sont détruits.

L’implémentation de Windows est très basique et je pense que ce serait sortingvial pour vous d’écrire une implémentation plus performante.