.net - example - stopwatch()
¿Puede la clase de.NET Stopwatch ser ESTA terrible? (3)
Estoy creando una aplicación que necesita un tiempo bastante ajustado, y la clase Cronómetro es la solución perfecta. Sin embargo, a veces me di cuenta de que, cuando se ejecutaba en una pequeña PC de panel, los valores del Cronómetro estaban muy lejos. Agregué algunas impresiones de depuración que monitorean el valor del cronómetro cada 200 ms o menos:
0: 00: 197
0: 00: 502
0: 00: 702
...
0: 03: 356
0:12:93
0:13:21
0: 13: 421
...
¿Cómo podría saltar de ~ 3 segundos a ~ 13 segundos? Ahora veo que la función subyacente QueryPerformanceCounter () tiene errores ( Cuidado con QueryPerformanceCounter () ), pero tengo la sensación de que algo más está sucediendo aquí.
Cualquier apreciación es apreciada.
Actualizar:
Aquí hay un poco más de detalles sobre mi código: es bastante sencillo. Es una aplicación WPF que crea un nuevo objeto de Stopwatch
al inicio y luego lo inicia a través de Start()
. Entonces creo un DispatcherTimer
, así:
displayTimer = new DispatcherTimer();
displayTimer.Tick += display_Tick;
displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN;
donde el lapso de tiempo es de 200 ms. Mi código de depuración simplemente imprime el valor del objeto Stopwatch
cada vez que el dispatchTimer
marca.
Actualización2:
Un artículo de soporte de Microsoft divertido es que el valor del contador de rendimiento puede saltar inesperadamente .
El tiempo exacto en el hardware de la PC no es sencillo, punto. Aquí hay algunos recursos, que muestran algunas experiencias con la sincronización en Windows, que serán la implementación subyacente de cualquier contador en un entorno de Windows, incluido .NET:
- Resultados de una investigación rápida sobre el tiempo en Win32
- Necesita ayuda con QueryPerformanceCounter y procesadores duales
Explica por qué a veces obtendrá una resolución de 10 ms, dependiendo de qué llamada de sistema use, y arroja un poco más de luz sobre por qué QueryPerformanceCounter está "lleno de errores". Uno de los puntos señalados es que los modos de ahorro de energía / velocidades variables de la CPU pueden interferir con estos tiempos.
Un concepto relacionado con esto es "bloquear el paso del tiempo" en simulaciones físicas en tiempo real. Si busca en Google para eso, puede obtener algunas ideas sobre cómo solucionar los problemas que tiene. El concepto básico es que tiene pasos de tiempo fijos y realiza una especie de implementación de productor / consumidor para sus funciones de tiempo / actualización. No sé si esto se aplica a su dominio específico, pero se considera una de las mejores prácticas en videojuegos.
Quizás estoy malinterpretando su pregunta, pero ¿necesita simplemente medir intervalos de tiempo o necesita ejecutar código en esos intervalos? Si necesita ejecutar código, revise la clase System.Timers.Timer, ya que es seguro para subprocesos y debería funcionar bien para multiprocesadores.
Actualizar (después de ver su registro)
Como ya mencionó, la clase Stopwatch
usa la función QueryPerformanceCounter
debajo. En la sección de Comentarios , MSDN dice:
En una computadora multiprocesador , no debería importar a qué procesador se llama. Sin embargo, puede obtener diferentes resultados en diferentes procesadores debido a errores en el sistema básico de entrada / salida (BIOS) o la capa de abstracción de hardware (HAL). Para especificar la afinidad del procesador para un hilo, use la función SetThreadAffinityMask.
Como está utilizando el Dispatcher, QueryPerformanceCounter
puede no ejecutarse en la misma CPU cada vez que consulta el tiempo transcurrido.
Posiblemente puede verificar si el problema mencionado en MSDN es la razón de su problema especificando una afinidad de procesador para su proceso, por ejemplo, llamando a su archivo ejecutable utilizando el comando de start
. 10 segundos me parece un gran retraso entre las CPU, pero la documentación es muy vaga sobre cuán grande puede ser la diferencia. El siguiente comando unirá su aplicación a la primera CPU:
> start.exe /AFFINITY 1 program.exe
Si esto resuelve el problema, es posible que desee echar un vistazo a la solución sugerida, es decir, llamar a la función SetThreadAffinityMask
antes de consultar el objeto Stopwatch
.
Su comentario dice que está utilizando un DispatcherTimer
WPF.La documentation de esa clase establece:
No se garantiza que los temporizadores se ejecuten exactamente cuando se produce el intervalo de tiempo , pero se garantiza que no se ejecutarán antes de que se produzca el intervalo de tiempo. Esto se debe a que las operaciones de DispatcherTimer se colocan en la cola de Dispatcher al igual que otras operaciones. Cuando se ejecuta la operación DispatcherTimer depende de los otros trabajos en la cola y sus prioridades.
Esto significa que el evento del temporizador puede llegar demorado, especialmente si el despachador está ocupado con otras tareas. ¿Colocó otras cosas en la cola del despachador que evitará que el evento se dispare antes?