2008-08-12 11 views
14

Estoy tratando de reconstruir una aplicación de metrónomo anterior que se escribió originalmente usando MFC en C++ para escribir en .NET usando C#. Uno de los problemas con los que me estoy metiendo es conseguir que el temporizador "marque" con la precisión suficiente.Obtener tics precisos de un temporizador en C#

Por ejemplo, asumiendo un BPM (latidos por minuto) fácil de 120, el temporizador debe marcar cada 0,5 segundos (o 500 milisegundos). El uso de esto como base para los tics, sin embargo, no es del todo exacto ya que .NET solo garantiza que su temporizador no marque antes de que haya transcurrido el tiempo transcurrido.

Actualmente, para evitar el mismo ejemplo de 120 BPM utilizado anteriormente, estoy configurando los ticks a algo así como 100 milisegundos y solo estoy reproduciendo el clic en cada 5 tic del temporizador. Esto mejora bastante la precisión, pero si se siente como un truco.

Entonces, ¿cuál es la mejor manera de obtener tics precisos? Sé que hay más temporizadores disponibles que el temporizador de formularios de Windows que está disponible en Visual Studio, pero no estoy realmente familiarizado con ellos.

Respuesta

9

Hay tres clases de temporizador llamadas 'Timer' en .NET. Parece que está utilizando el formulario de Windows Forms, pero en realidad puede encontrar que la clase System.Threading.Timer es más útil, pero tenga cuidado porque devuelve la llamada a un hilo del grupo, por lo que no puede interactuar directamente con su formulario desde la devolución de llamada.

Otro enfoque podría ser p/invocar a los temporizadores multimedia Win32 - timeGetTime, timeSetPeriod, etc.

Una rápida de Google encontraron esta, que podría ser útil http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'Multimedia' (temporizador) es la palabra de moda para buscar en este contexto.

1

¿Qué utiliza la aplicación C++? Siempre puede usar lo mismo o ajustar el código del temporizador de C++ en una clase C++/CLI.

0

Las clases de temporizador pueden comenzar a comportarse de manera extraña cuando el código del evento 'tick' del temporizador no se termina de ejecutar en el momento en que se produce el siguiente 'tic'. Una forma de combatir esto es desactivar el temporizador al comienzo del evento tick, luego volver a habilitarlo al final.

Sin embargo, este enfoque no es adecuado en casos donde el tiempo de ejecución del código 'tic' no es aceptable en el tiempo del tic, ya que el temporizador se desactivará (sin contar) durante ese tiempo.

Si la desactivación del temporizador es una opción, entonces usted puede también conseguir el mismo efecto mediante la creación de un subproceso independiente que se ejecuta, duerme durante x milisegundos, ejecuta, duerme, etc ...

+0

Pero entonces sólo se puede estar seguro de que el hilo se duerme al menos x millioseconds; el programador de hilos no certifica que el hilo se ejecutará en el recuento exacto de millesond – Wilhelm

+0

Derecha. Estoy de acuerdo con lo que dijiste sobre no poder determinar la hora del próximo tic. El punto de lo que estoy diciendo es que no desea ejecutar el código del evento tick de un tic previo cuando se produce el siguiente tic. –

0

System.Windows.Forms.Timer se limita a una precisión de 55 milisegundos ...

+1

¿Hay alguna documentación oficial al respecto? – MajesticRa

+0

@MajesticRa: http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx Consulte el texto en el cuadro amarillo –

+0

¡Gracias! Cuando lo escribí, me olvidé de señalar que yazanpro significa exactamente el temporizador de Forms. Y pensó que estaba hablando de temporizadores en general. ¡Pero muchas gracias por su respuesta! – MajesticRa

1

He tenido este problema al desarrollar un proyecto reciente de registro de datos. El problema con los temporizadores .NET (windows.forms, system.threading y system.timer) es que solo tienen una precisión de hasta 10 milisegundos, lo que se debe a la programación del evento incorporada en .NET, creo. (Estoy hablando de .NET 2 aquí). Esto no era aceptable para mí, así que tuve que usar el temporizador multimedia (debe importar el dll). También escribí una clase contenedora para todos los temporizadores y así puede cambiar entre ellos si es necesario usando cambios mínimos de código.Visita mi blog aquí: http://www.indigo79.net/archives/27

1

Otra posibilidad es que hay un error en la aplicación de WPF de DispatcherTimer (hay un desajuste entre milisegundos y garrapatas que causan inexactitud potencial dependiendo de la hora exacta ejecución del proceso), como se evidencia por debajo :

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 
Cuestiones relacionadas