2009-06-17 15 views
18

¿Es normal que el cronómetro devuelva valores negativos? La muestra de código siguiente puede usarse para reproducirla.System.Diagnostics.Stopwatch devuelve números negativos en Elapsed ... properties

while (true) 
     { 
      Stopwatch sw = new Stopwatch(); 
      sw.Start(); 
      sw.Stop(); 

      if (sw.ElapsedMilliseconds < 0) 
       Debugger.Break(); 

     } 

El único lugar donde puedo reproducir los números negativos es mi máquina virtual (organizada por Hyper-V en una máquina de 8 núcleos)

+0

¿Cuál es el valor de ElapsedMilliseconds cuando es negativo? –

+18

Desinstale su condensador de flujo. –

+0

Cambia todo el tiempo. Puede tomar varios giros de bucle para que ocurra un número negativo. –

Respuesta

17

Ésta es una bug. No parece tener mucha atención al respecto, así que sugiero seguir con ese informe.

El uninspiring workaround parece ignorar los valores negativos:

long elapsedMilliseconds = Math.Max(0, stopwatch.ElapsedMilliseconds); 
+0

Parece que la única forma de reproducirlo es utilizar una máquina virtual. He intentado con el código en varias máquinas de escritorio: sin éxito –

+4

Este método alternativo solo elimina los valores negativos, no elimina los valores positivos extremadamente altos (falsos) que también devuelve. –

+0

Está funcionando en .NET 4, pero es difícil de creer que la falla todavía exista en .NET 3.5 :( – vtortola

0

El "cerrado como se ha corregido" resolución sobre la Microsoft Connect Bug debe significar que lo arreglaron en .NET 4. Me he encontrado el código de repro de mi escritorio y una VM durante varios minutos y no obtuve valores negativos.

+3

Es no completamente corregido en .NET 4; mira mis comentarios en el informe Connect y en mi respuesta ... –

10

Descompilé la clase Stopwatch en .NET 2.0 y .NET 4.0 usando Reflector y luego comparé las diferencias para ver cómo se corrigió. Además de la adición del nuevo método de reinicio, esto es todo lo que encontré para una diferencia:

public void Stop() 
{ 
    if (this.isRunning) 
    { 
     long num2 = GetTimestamp() - this.startTimeStamp; 
     this.elapsed += num2; 
     this.isRunning = false; 
// THE NEXT 4 LINES ARE NEW IN .NET 4.0: 
     if (this.elapsed < 0L) 
     { 
      this.elapsed = 0L; 
     } 
    } 
} 

Así que, básicamente, se pusieron pasaron a 0 en el método Stop si el valor es negativo. Todavía hay errores en mi humilde opinión:

  1. ¿Qué pasa si el usuario lee cualquiera de las propiedades transcurridas antes de detener el cronómetro? Aún puede obtener un valor negativo.
  2. Restablecer a 0 no es correcto. ¡Ha transcurrido un tiempo de, aunque haya sido solo unos pocos microsegundos!
  3. No hace nada para manejar los valores positivos positivos inusualmente grandes que yo y otros hemos informado.

EDIT: Desafortunadamente, # 2 y # 3 son más allá del poder de .NET Framework:

Aquí está el núcleo del problema: desde MSDN en QueryPerformanceCounter que es el API utilizada por el cronómetro clase:

En una computadora multiprocesador, no debería importar qué procesador se llama . Sin embargo, se puede obtener diferentes resultados en diferentes procesadores debido a los 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.

No utilice el cronómetro .NET 4.0 y suponga que el problema está solucionado. No lo es y no pueden hacer nada al respecto a menos que quieras que arruinen tu afinidad con el hilo. De la documentación de la clase Cronómetro:

En una computadora multiprocesador, no importa en qué procesador se ejecuta el subproceso . Sin embargo, debido a errores en el BIOS o el Hardware capa de abstracción (HAL), puede obtener resultados diferentes en temporización procesadores diferentes.Para especificar la afinidad del procesador para un hilo, use el método ProcessThread.ProcessorAffinity.

-1

La única solución que he encontrado para conseguir el tiempo transcurrido correcta en una máquina virtual es (VB):

Dim tstart AS DateTime = Now 
...executing code... 
Dim telapsed = (Now - tstart).TotalMilliseconds 
+2

¿Por qué el voto abajo? –

+0

downvote no fui yo ... pero quizás porque la propiedad 'Ticks' de' DateTime.Now 'no tiene la misma resolución que un' Cronómetro' (hasta donde recuerdo, los 'Ticks' de' DateTime.Now' solo se actualizan cada 15ms) –

+0

Tenga en cuenta que la propiedad 'Now' saltará adelante o adelante dramáticamente durante el día También es mucho, mucho más lento que 'UtcNow '. – James

Cuestiones relacionadas