2010-11-18 17 views
15

Quiero usar el temporizador de resolución más alto posible usando C#. Por ejemplo, quiero plantear un evento cada 11 ticks (he oído que el tick es el contador más alto posible en la PC). Intenté el cronómetro y descubrí que el tiempo mínimo transcurrido es en milisegundos. Miró el cronómetro, pero el cronómetro no plantea eventos.Suceso de elevación en intervalo/temporizador de alta resolución

Gracias.

+2

¿Por qué en el mundo le gustaría plantear un evento cada 11 tics? ¿Estás usando esto para crear perfiles de código? –

+5

Usted no está respondiendo preguntas. 11 tic es solo un ejemplo. todo lo que quiero es un temporizador de alta resolución. si es posible, hasta 1 tick. y sí, estoy desarrollando una aplicación para hacer algún punto de referencia. –

+0

Es por eso que apoyo los comentarios de downvoting –

Respuesta

18

El uso de un temporizador multimedia debería proporcionarle aproximadamente 1000 eventos por segundo. Este código debería ayudarte en el camino.

 public delegate void TimerEventHandler(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2); 

    /// <summary> 
    /// A multi media timer with millisecond precision 
    /// </summary> 
    /// <param name="msDelay">One event every msDelay milliseconds</param> 
    /// <param name="msResolution">Timer precision indication (lower value is more precise but resource unfriendly)</param> 
    /// <param name="handler">delegate to start</param> 
    /// <param name="userCtx">callBack data </param> 
    /// <param name="eventType">one event or multiple events</param> 
    /// <remarks>Dont forget to call timeKillEvent!</remarks> 
    /// <returns>0 on failure or any other value as a timer id to use for timeKillEvent</returns> 
    [DllImport("winmm.dll", SetLastError = true,EntryPoint="timeSetEvent")] 
    static extern UInt32 timeSetEvent(UInt32 msDelay, UInt32 msResolution, TimerEventHandler handler, ref UInt32 userCtx, UInt32 eventType); 

    /// <summary> 
    /// The multi media timer stop function 
    /// </summary> 
    /// <param name="uTimerID">timer id from timeSetEvent</param> 
    /// <remarks>This function stops the timer</remarks> 
    [DllImport("winmm.dll", SetLastError = true)] 
    static extern void timeKillEvent( UInt32 uTimerID); 

Detenga estos temporizadores después de ejecutarlos. Son bastante pesados ​​en su sistema *. Capture todas las excepciones y no las deje escapar de su controlador de eventos.

* ¡Comenzar más de 5 temporizadores ralentizará seriamente la mayoría de los sistemas! Ejecuta la menor cantidad posible de código en los manejadores de eventos y asegúrate de que el código de ejecución sea más rápido que 1 milisegundo o enfrenta serios problemas. Empecé un delegado cada 10-50 ticks para aumentar la visualización de una etiqueta.

Un cambio de hilo normal que se produce en Thread.Sleep dejará una ranura de hilo libre de su código y tardará unos 40 milisegundos. También puede aumentar la frecuencia de cambio de hilo con algunas llamadas al kernel de NT, pero por favor, no haga eso.

4

Las diferentes clases de temporizador utilizan una granularidad mayor. Ambos Threading.Timer y Timers.Timer utilizan 1/64 de segundo, que es 15.625 milisegundos.

Si el 'tic' al que se refiere es el tic de 100 nanosegundos, utilizado por la clase DateTime, la clase TimeSpan y la salida por el cronómetro, entonces la longitud de 11 ticks que está solicitando es de 1.100 nanosegundos o 1.1 microsegundos. Que yo sepa, no hay un temporizador incorporado que le proporcione esa resolución. Si realmente quieres que ocurra un evento cada 1.1 microsegundos, vas a tener que eliminar la idea de un "temporizador" y, en cambio, pensar en términos de un breve retraso. Convierta un subproceso en una prioridad alta y ejecute su evento en un bucle. No llame a Thread.Sleep(), ya que creo que 1.1 microsegundos es más pequeño que el timeslice del planificador del sistema. Tendrás que hacer un bucle de retraso en su lugar.

Además, tenga en cuenta que el intervalo de tiempo que está preguntando es muy, muy pequeño. 1,1 microsegundos son solo 2,200 ciclos de procesador en un procesador de 2 GHz. No es una cantidad insignificante, pero no es mucho tiempo para hacer mucho trabajo. Si está hablando de la frase 1 que dijo en su comentario, eso son solo 200 ciclos de procesador: es tiempo suficiente para hacer algunas docenas de operaciones matemáticas, y quizás llamar a una función.

+0

El tic que me interesa es el cronómetro.frecuencia un poco tic. En otras palabras, tic del ciclo del procesador. –

+3

Tic del ciclo del procesador, p. Ej., 2 GHz? Eso es 0.5 nanosegundos. Todas, excepto las instrucciones de procesador más simples toman múltiples ciclos de procesador. Por ejemplo, ¿podría incrementar un entero en un ciclo, pero una instrucción 'if'? De ninguna manera, eso es del orden de 10 ciclos de procesador, 5 nanosegundos. No hay forma de verificar un temporizador a esa frecuencia, y mucho menos de hacer un trabajo. –

+4

Espera, retiro eso. Hay una forma de tener un temporizador con una frecuencia de 1 ciclo de procesador: establezca el subproceso como de alta prioridad y haga 'while (true) {...}' –

14

En primer lugar, debe tener en cuenta que es extremadamente difícil, si no imposible, hacer el tiempo exacto en una computadora debido a las limitaciones expuestas tanto por el hardware como por el software. La buena noticia es que este tipo de precisión rara vez es necesario. Diez tics es un increíblemente pequeño cantidad de tiempo. Se está haciendo muy poco trabajo por parte de la CPU en este intervalo, y nunca va a ser estadísticamente significativo.

Como referencia, el reloj de Windows tiene una precisión de alrededor de 10 milisegundos (menos en versiones anteriores). Envolver su código con llamadas al DateTime.UtcNow no va a hacer nada mejor que eso.

En su pregunta, usted habla de querer "plantear un evento". El problema es que el único tipo de objeto de mantenimiento del tiempo que genera un evento en intervalos específicos es el objeto Timer. Está disponible en 3 encarnaciones diferentes en .NET Framework (System.Timers.Timer, System.Threading.Timer y System.Windows.Forms.Timer), todos los cuales tienen sus propios escenarios de uso únicos y peculiaridades relativas, pero ninguno de ellos garantiza precisión en cualquier lugar cerca de lo que está pidiendo . Ni siquiera están diseñados para hacerlo, y tampoco hay funciones equivalentes expuestas por la API de Windows que proporcionen este tipo de precisión.

La razón por la que pregunté por qué querías hacer esto y si estás tratando de establecer un punto de referencia es porque eso cambia todo el juego. .NET Framework (a partir de la versión 2.0) proporciona un Stopwatch object que está expresamente diseñado para medir con precisión el tiempo transcurrido para una situación como la evaluación comparativa o el perfil de rendimiento. El Stopwatch simplemente ajusta las funciones de la API de Windows QueryPerformanceFrequency y QueryPerformanceCounter (que debe confirmar mi sugerencia en cuanto a su uso previsto). Solíamos tener P/Invocar estas funciones para acceder a este tipo de funcionalidad en versiones anteriores del Framework, pero ahora está cómodamente integrado. Si necesita un temporizador con una resolución relativamente alta para el benchmarking, el Stopwatch es su mejor opción . En teoría, puede proporcionarle un tiempo de sub-microsegundo.

Pero no está exento de problemas. No genera ningún evento, por lo que si su diseño actual depende del manejo de eventos, tendrá que volver a pensarlo. Y, , tampoco está garantizado que sea perfectamente exacto. Claro, puede tener la mayor resolución posible teniendo en cuenta las limitaciones de hardware, pero eso no significa que necesariamente cumplirá con los requisitos establecidos. Por ejemplo, puede no ser confiable en un sistema de procesador múltiple donde Start y Stop tienen que ejecutarse en el mismo procesador. No debería importar, pero it does. También es subject to being unreliable en procesadores que pueden acelerar su velocidad de reloj hacia arriba y hacia abajo. Y me atrevería a mencionar que la llamada al QueryPerformanceCounter va a llevar algo de tiempo, unos 5 microsegundos incluso en un moderno procesador de 2+ GHz, lo que evita que realmente pueda alcanzar esa temporización de sub microsegundos que sonaba bien en teoría. Por otra parte, cualquier generador de perfiles de código razonable consideraría que esa cantidad de tiempo es insignificante porque, bueno, es.
(Ver también: http://www.devsource.com/c/a/Techniques/High-Performance-Timing-under-Windows/2/)

+0

Gracias por la explicación larga. si, por ejemplo, uso un sistema operativo en tiempo real, ¿puedo obtener ese tipo de temporizador de alta precisión? Windows Server es un sistema operativo en tiempo real? Gracias. –

+2

Si usa un RTOS, es posible que tenga la posibilidad de tener un temporizador de alta resolución. Sin embargo, un sistema operativo en tiempo real no significa que las cosas sucedan de inmediato, sino que ocurren dentro de un límite de tiempo conocido, por lo que es posible que aún no pueda programar el tiempo tan preciso como está pensando. En cuanto a que Windows es un RTOS, definitivamente no. –

+1

@publicENEMY: la respuesta de David Yaw es correcta. Incluso las versiones de servidor de Windows no son sistemas operativos en tiempo real (ejecuto Windows Server como una estación de trabajo, no es muy diferente). También vale la pena señalar que incluso si superas las limitaciones del software expuestas por el sistema operativo, aún tendrás que lidiar con las limitaciones del hardware común para PC. Si está considerando cambiar los sistemas operativos, debe * haber * una mejor manera de hacer lo que está tratando de lograr. –

Cuestiones relacionadas