2008-08-12 6 views
14

J'essaie de reconstruire une ancienne application de métronome écrite à l'origine à l'aide de MFC en C++ pour être écrite en .NET à l'aide de C#. L'un des problèmes que je rencontre est de faire en sorte que la minuterie «cochée» soit suffisamment précise. Par exemple, en supposant un BPM facile (battements par minute) de 120, le temporisateur devrait cocher toutes les 5 secondes (ou 500 millisecondes). L'utilisation de ce paramètre comme base pour les ticks n'est toutefois pas entièrement précise car .NET garantit uniquement que votre minuteur ne se déclenchera pas avant que le temps écoulé ne soit écoulé. Actuellement, pour contourner le problème pour le même exemple de 120 BPM utilisé ci-dessus, je règle les ticks à quelque chose comme 100 millisecondes et je ne joue que le clic sur chaque tick de 5ème timer. Cela améliore la précision un peu, mais si on se sent un peu un hack.Obtention de mesures précises à partir d'une minuterie en C#

Alors, quel est le meilleur moyen d'obtenir des tiques précises? Je sais qu'il y a plus de minuteurs disponibles que le minuteur de formulaires Windows qui est facilement disponible dans Visual Studio, mais je ne les connais pas vraiment.

Répondre

9

Il existe trois classes de temporisation appelées 'Timer' dans .NET. Il semble que vous utilisiez le Windows Forms, mais en fait vous pourriez trouver la classe System.Threading.Timer plus utile - mais attention car il rappelle sur un thread de pool, donc vous ne pouvez pas interagir directement avec votre formulaire depuis le rappel.

Une autre approche pourrait être de p/invoquer les minuteries multimédia Win32 - timeGetTime, timeSetPeriod, etc.

Un rapide Google a trouvé ce qui pourrait être utile http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'Multimédia' (minuterie) est le mot à la recherche dans ce contexte.

1

Qu'est-ce que l'application C++ utilise? Vous pouvez toujours utiliser la même chose ou placer le code temporel de C++ dans une classe C++/CLI.

0

Les classes de temporisation peuvent commencer à se comporter bizarrement lorsque le code d'événement du temporisateur 'tick' n'est pas terminé lorsque le 'tick' suivant se produit. Une façon de lutter contre cela est de désactiver la minuterie au début de l'événement tick, puis de le réactiver à la fin. Cependant, cette approche ne convient pas dans les cas où le temps d'exécution du code 'tick' n'est pas une erreur acceptable dans le timing de la graduation, puisque la temporisation sera désactivée (sans compter) pendant ce temps.

Si la désactivation de la minuterie est une option, vous pouvez également obtenir le même effet en créant un thread séparé qui exécute, pour dort x millisecondes, exécute, dort, etc ...

+0

Mais alors vous ne pouvez être sûr que le fil dort au moins x millioseconds; le planificateur de thread ne certifie pas que le thread s'exécutera exactement au nombre de milleond – Wilhelm

+0

Right. Je suis d'accord avec ce que vous avez dit à propos de ne pas être capable de déterminer l'heure de la prochaine tique. Le point de ce que je dis est que vous ne voulez pas exécuter le code d'événement tick d'une coche précédente quand votre tick suivant se produit. –

0

System.Windows.Forms.Timer est limitée à un précision de 55 millisecondes ...

+1

Y a-t-il une documentation officielle à ce sujet? – MajesticRa

+0

@MajesticRa: http://msdn.microsoft.com/fr-fr/library/system.windows.forms.timer.aspx Voir le texte en case jaune –

+0

Merci! Quand je l'ai écrit, j'ai raté le point que yazanpro signifie exactement minuterie. Et pensait qu'il parlait des minuteurs en général. Mais merci beaucoup pour votre réponse! – MajesticRa

1

J'ai rencontré ce problème lors du développement d'un projet récent d'enregistrement de données. Le problème avec les temporisateurs .NET (windows.forms, system.threading, et system.timer) est qu'ils sont seulement précis jusqu'à environ 10 milli secondes, ce qui est dû à la programmation d'événements intégrée dans .NET je crois. (Je parle de .NET 2 ici). Ce n'était pas acceptable pour moi et j'ai donc dû utiliser la minuterie multimédia (vous devez importer la DLL). J'ai également écrit une classe wrapper pour tous les timers et ainsi vous pouvez basculer entre eux si nécessaire en utilisant des changements de code minimes.Consultez mon blog ici: http://www.indigo79.net/archives/27

1

Une autre possibilité est qu'il ya un bogue dans la mise en œuvre WPF de DispatcherTimer (il y a un décalage entre les millisecondes et les tiques provoquant l'inexactitude potentielle en fonction du temps d'exécution du processus exact), comme en témoigne ci-dessous :

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,143

class DispatcherTimer 
{ 
    public TimeSpan Interval 
    { 
     set 
     { 
      ... 
      _interval = value; 
      // Notice below bug: ticks1 + milliseconds [Bug1] 
      _dueTimeInTicks = Environment.TickCount + (int)_interval.TotalMilliseconds; 
     } 
    } 
} 

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs

class Dispatcher 
{ 
    private object UpdateWin32TimerFromDispatcherThread(object unused) 
    { 
     ... 
     _dueTimeInTicks = timer._dueTimeInTicks; 
     SetWin32Timer(_dueTimeInTicks); 
    } 

    private void SetWin32Timer(int dueTimeInTicks) 
    { 
     ... 
     // Notice below bug: (ticks1 + milliseconds) - ticks2 [Bug2 - almost cancels Bug1, delta is mostly milliseconds not ticks] 
     int delta = dueTimeInTicks - Environment.TickCount; 
     SafeNativeMethods.SetTimer( 
      new HandleRef(this, _window.Value.Handle), 
      TIMERID_TIMERS, 
      delta); // <-- [Bug3 - if delta is ticks, it should be divided by TimeSpan.TicksPerMillisecond = 10000] 
    } 
} 

http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/SafeNativeMethodsCLR.cs,505

class SafeNativeMethodsPrivate 
{ 
    ... 
    [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)] 
    public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, NativeMethods.TimerProc lpTimerFunc); 
} 

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx

uElapse [in] 
Type: UINT 
The time-out value, in milliseconds. // <-- milliseconds were needed eventually 
Questions connexes