9
votes

Timing haute fréquence .NET

Je cherche à créer un fil de rappel à haute fréquence. Nous avons essentiellement besoin d'une fonction pour exécuter à un intervalle régulier à haute fréquence (jusqu'à 100Hz). Je me rends compte que Windows a une tranche d'exécution de fil normale est ~ 15ms. Je voudrais spécifier un intervalle régulier pouvant être plus rapide que 15 ms.

C'est ce que j'essaie d'accomplir. J'ai un périphérique externe qui doit être envoyé à un certain intervalle. L'intervalle est variable en fonction de la situation. Je m'attends à ce que je n'aurais plus besoin de plus qu'un taux de message de 100Hz (10 ms). P>

i peut bien entendu mettre en œuvre une boucle de spin, mais j'espérais qu'il y avait une solution qui ne nécessiterait pas tant de gaspillage Ressources. P>

Les liens fournis à des questions / réponses ne résolvent pas cette question. Si je conviens que la question a été posée de plusieurs manières différentes, il n'ya pas eu une bonne solution qui a résolu le problème. P>

La plupart des réponses fournies parlent à l'utilisation de chronomètres et effectuant la tâche de synchronisation manuellement, ce qui est totalement trop important de la CPU. Les seules solutions viables utilisaient les minuteries multimédia qui ont eu quelques pièges comme Haans mentionnés. J'ai trouvé une autre solution cependant que je vais ajouter ci-dessous. Je ne connais pas les pièges à ce moment-là, mais je prévois de faire des tests et des recherches. Je suis toujours intéressé par les commentaires concernant la solution. P>


Appel Winapi via P>

[DllImport("kernel32.dll")]
static extern bool CreateTimerQueueTimer(out IntPtr phNewTimer,
   IntPtr TimerQueue, WaitOrTimerDelegate Callback, IntPtr Parameter,
   uint DueTime, uint Period, uint Flags);

// This is the callback delegate to use.
public delegate void WaitOrTimerDelegate (IntPtr lpParameter, bool TimerOrWaitFired);

[DllImport("kernel32.dll")]
static extern bool DeleteTimerQueueTimer(IntPtr TimerQueue, IntPtr Timer,
   IntPtr CompletionEvent);


0 commentaires

4 Réponses :


1
votes

Vous voudrez peut-être essayer le chronomètre , qui utilise les mêmes minuteries de haute performance utilisées par DirectX.


8 commentaires

Quand disponible. N'oubliez pas de vérifier Ishighresolution


Cela devrait être vrai sur le matériel le plus moderne que je présume.


Oui, mais vous auriez toujours besoin de le vérifier, au cas où.


Je ne sais pas comment cela vous aide. J'ai besoin d'un rappel particulier pour se produire à un intervalle <15ms. Le chronomètre peut me donner un timing précis, cependant, je devrais graver la CPU dans une boucle en vérifiant constamment le temps afin d'atteindre l'objectif qui est ce que j'essaie d'éviter (la combustion de la CPU).


@galford: Vous devriez vraiment dire dans votre question title que vous souhaitez un rappel précisément planifié, et pas seulement une mesure de minuterie précise ("chronométrage" peut signifier l'une ou l'autre)


@BEN VOIGT: En effet, je vais mettre à jour la question. Je suppose que je suppose que cela soit évident avec l'explication de la tranche de temps de thread avec l'exigence d'intervalle de message haute fréquence.


@ Galford13x: tandis que Chronomètre n'a pas de rappel intégré, vous pouvez toujours essayer de le vérifier dans une boucle, vous pourrez peut-être obtenir le timing sous-milliseconde. Je peux trouver du code si vous voulez. De toute façon, en utilisant une langue non déterministe et gc comme c # n'est pas la meilleure approche ici pour faire une programmation en temps réel. Une meilleure approche serait d'écrire cette partie en C / C ++.


@Ali B: J'ai déjà une solution que vous mentionnez à l'aide d'une boucle de spin. Le problème que j'ai parlé auparavant, ce sont les ressources de la CPU qu'elle exige. Comment écrire cette partie dans l'aide C / C ++? Je peux toujours écrire le code en C # en utilisant des objets sans pilote pour arrêter la GC sur cette partie ou même arrêter GC sur tous les objets accessibles par le thread sur via C #. Je n'ai pas besoin d'une opération de temps réel stricte. Si une exécution arrive 2 msec tard, mais la prochaine est 2msec tôt, mon objectif sera accompli.



0
votes

Notez que la partie de la raison de la raison pour laquelle 15 ms est le minimum effectif TIME est qu'il peut prendre cet ordre de grandeur pour échanger votre processus, échangez un autre dans, laissez-le avoir un peu de temps Pour faire n'importe quoi, écrasez-le et échangeez-le à nouveau. En supposant que vous avez plusieurs cœurs, consacrez-vous à la fois à votre updateur de 100Hz est probablement préférable, surtout si vous savez que vous souhaitez sauter quelques cycles pour une collection de déchets générés manuellement et / ou utilisez C / C ++ comme suggéré.


2 commentaires

Ne pas mentionner que le contexte d'échange se produit à une fréquence minimale spécifique différente des versions du client à serveur de Windows. Voir Windows Quantum


La raison pour laquelle les 15 ms jouent une partie est comme Haans mentionnée, c'est-à-dire une tranche de temps minimum donnée à un thread par Windows. Bien sûr, comme il a dit que la plate-forme et la version du système d'exploitation dépendent. Vous pouvez tester cela en écrivant une application multithreadiée qui enregistre le temps et utilise le thread.sleep (2) pendant quelques secondes, puis imprimez tous les temps enregistrés dans le fil. Vous remarquerez qu'ils sont ~ 15.6ms séparés. Bien sûr, un thread peut utiliser moins de 15 ms simplement en appelant thread.sleep (0) abandonner le reste de sa tranche à quelque chose de plus important.



10
votes

Il existe un lot d'informations inexactes réparties dans les liens et les commentaires. Oui, par défaut, l'interruption de la tique de l'horloge est de 1/64 secondes = 15,625 ms à la plupart des machines, mais cela peut être changé. Aussi la raison pour laquelle certaines machines semblent fonctionner sur un autre taux. L'API Windows Multimédia, disponible à partir de WinMM.DLL vous permet de bricoler.

Qu'est-ce que vous ne voulez pas faire, c'est utiliser un chronomètre. Vous n'obtiendrez qu'une mesure d'intervalle précise de celui-ci lorsque vous l'utilisez dans une boucle chaude qui vérifie constamment si l'intervalle est passé. Windows traite un tel fil méchant lorsque sa quantum expire, le thread ne sera pas reprogrammé pour fonctionner pendant un moment lorsque d'autres threads sont en concurrence pour le processeur. Cet effet est facile à manquer puisque vous ne débogez généralement pas votre code avec d'autres processus en cours d'exécution et de brûlure de l'heure de la CPU activement.

La fonction que vous souhaitez utiliser est TIITEEvent (), il fournit une minuterie très précise qui peut aller aussi bas que 1 milliseconde. Il est auto-corrige, réduisant l'intervalle si nécessaire (et possible) pour rattraper le retard lorsque le rappel précédent a été retardé en raison de contraintes de planification. Méfiez-vous cependant qu'il est difficile d'utiliser, le rappel est fabriqué à partir d'un thread de threadpool, similaire à System.threading.Timer, alors assurez-vous d'utiliser un verrouillage sûr et de faire des contre-mesures garantissant que vous ne recevez pas de problèmes de ré-entrouverness.

Une approche complètement différente est le temps dépend du temps (), il modifie le taux d'interruption d'horloge. Cela a de nombreux effets secondaires, pour un thread.sleep () devient plus précis. Qui a tendance à être la solution la plus facile puisque vous pouvez rendre cette synchrone. Il suffit de dormir pendant 1 msec et vous obtiendrez le taux d'interruption. Certains code à jouer qui démontrent la façon dont il fonctionne: xxx

sortie sur ma machine: xxx

Attention cependant D'un problème avec cette approche, si un autre processus de votre machine a déjà abaissé le taux d'interruption d'horloge inférieur à 10 msec, vous n'obtiendrez pas une seconde de ce code. C'est très difficile à traiter, vous ne pouvez que le rendre véritablement en sécurité en demandant un taux de 1 ms et dormir pour 10. Ne fonctionne pas sur une machine à piles. FAIRE FAIRE LES TAIMESTEVENT ().


3 commentaires

Merci Hans, je peux toujours apprécier vos réponses et vos conseils. J'ai effectivement trouvé quelque chose qui résout mon problème tout en recherchant l'API que vous mentionnez. Qui utilise la créationAmerQuetimer. Cela semble me donner la résolution avec l'API que vous mentionnez, mais sans l'inconvénient de la concurrence de modifier les événements de synchronisation. Un autre avantage supplémentaire est qu'il provient de Kernal32.dll. Toute réflexion concernant la classe API CreateTimerQuerQuerTimer?


Ouais, c'est l'alternative recommandée pour TimesetEvent (). Je ne sais pas assez de sa capacité à modifier le taux d'interruption de l'horloge, vous devrez expérimenter.


Voyant comme s'il ne peut être fourni plus de réponses. Je marquais cela comme la réponse. Il fournit une solution qui fonctionnerait pour ma situation.



0
votes

thread.sleep () code> ne semble pas être aussi inexact que vous pouvez penser à des intervalles aussi petits, sous Windows 8.1 x64 I7 ordinateur portable .NET 4.0:

using System;
using System.Diagnostics;
using System.Threading;

namespace HighFrequency
{
    class Program
    {
        static void Main(string[] args)
        {
            var count = 0;
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            while(count <= 1000)
            {
                Thread.Sleep(1);
                count++;
            }
            stopwatch.Stop();
            Console.WriteLine("C# .NET 4.0 avg. {0}", stopwatch.Elapsed.TotalSeconds / count);
        }
    }
}


2 commentaires

À moins que quelque chose ait changé dans Win8 pour l'appel du sommeil, alors en général, le sommeil n'est pas un bon mécanisme pour garder le temps. De plus, le sommeil peut être imprévisible dans son minimum de synchronisation lorsque la minuterie haute fréquence est modifiée sur le système. En bout de ligne, pendant que vous obtenez ~ 10ms, lorsque je l'exécute sur ma machine Win7, je reçois 15,6 ms sur AVG. De plus, je ne cherchais pas des valeurs avgées, je cherchais un chronométrage cohérent précis, donc 12ms, 9 ms, 11 ms, 8 ms ne serait pas acceptable.


Vrai, je ne l'utiliserais pas pour garder le temps, juste pour des boucles de haute fréquence - je mesurerais le temps écoulé réel à l'aide d'un chronomètre - mais toujours pas une option si vous voulez une durée cohérente.