2009-08-20 22 views
15

He encontrado una prueba unitaria que está fallando intermitentemente porque el tiempo transcurrido no es el que espero.¿Cuán exacto es Thread.Sleep (TimeSpan)?

Un ejemplo de lo que esta prueba se ve como es:

Stopwatch stopwatch = new Stopwatch(); 
stopwatch.Start(); 

TimeSpan oneSecond = new TimeSpan(0, 0, 1); 

for(int i=0; i<3; i++) 
{ 
    Thread.Sleep(oneSecond); 
} 

stopwatch.Stop(); 

Assert.GreaterOrEqual(stopwatch.ElapsedMilliseconds, 2999); 

La mayoría de las veces esto pasa pero ha fracasado en al menos en una ocasión falló porque:

esperado: mayor o igual a 2999 Pero fue: 2998

No entiendo cómo podría ser menos de 3 segundos. ¿Hay algún problema de precisión con Thread.Sleep o quizás con Cronómetro que no conozco?

Al igual que una actualización de algunas de las preguntas a continuación. El escenario que se está probando por unidades es una clase que permite llamar a un método para realizar alguna acción y, si falla, esperar un segundo y recuperar ese método. La prueba que se muestra arriba es solo una aproximación de lo que está sucediendo.

Digamos que quería llamar a un método DoSomething() ... pero en el caso de que DoSomething() haga una excepción, puedo intentar volver a llamarlo hasta un máximo de 3 veces, pero esperar 1 segundo entre cada intento. El objetivo de la prueba unitaria, en este caso, es verificar que cuando solicitamos 3 intentos con 1 segundo de espera entre cada reintento, el tiempo total requerido es mayor a 3 segundos.

+0

Sólo por curiosidad, ¿por qué tienes una prueba de unidad que pone a prueba Thread.Sleep() de esta forma? Supongo que en realidad estás probando para ver si alguna otra actividad se completa en menos de 3 segundos, pero ¿por qué no simplemente llamar a Thread.Sleep (3000)? – MusiGenesis

+0

@MusiGenesis He actualizado la pregunta para intentar aclarar un poco las cosas. – mezoid

+0

Solo por información. Desde que descubrí este pequeño código, lo uso en todas partes: http://www.codeproject.com/KB/cs/highperformancetimercshar.aspx – sabiland

Respuesta

16

Su hilo está compartiendo el tiempo de CPU con otros hilos. El sueño terminará tan pronto como sea su turno nuevamente y el núcleo advierte que el tiempo de sueño ha transcurrido, por lo que no es tan preciso.

La carga de CPU, las prioridades de proceso, el número de subprocesos concurrentes, incluso de otros procesos, tendrán efecto sobre él.

+4

, pero si solo termina tan pronto como el núcleo advierta que el tiempo de espera ha transcurrido, ¿cómo es? Es posible que tome menos tiempo de lo que espero? 3010ms es comprensible y aceptable en mi caso, se está deteniendo antes de alcanzar el tiempo solicitado ... aunque por 1-2ms – mezoid

+2

La implementación probablemente establece un tiempo de activación unos pocos milisegundos antes de la hora especificada. Si no lo hiciera, * siempre * se despertaría más tarde. Al establecer el tiempo antes, tiene mejores probabilidades de llegar a un lugar cercano. –

+0

@mezoid: No creo que las arquitecturas de PC tengan una precisión mucho mayor. Los "ticks" de las PCs antiguas solo 18.2 veces por segundo, por lo que el reloj y el "conteo de ticks" se deben actualizar solo cada 52 ms. Creo que debe ser mucho más rápido hoy en día, pero todavía no estoy seguro de qué tan preciso es. – Havenard

8

Thread.Sleep no está destinado para ser usado con precisión. Realmente, la arquitectura de ventanas en sí misma no está pensada para este tipo de cosas.

1

En una aplicación donde quería dormir por al menos x milisegundos, que utiliza un código similar a:

public void Sleep(int milliseconds) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 

    while (stopwatch.ElapsedMilliseconds < milliseconds) 
    { 
     int timeout = milliseconds - stopwatch.ElapsedMilliseconds; 
     Thread.Sleep(timeout >= 0 ? timeout : 0); 
    } 

    stopwatch.Stop(); 
} 

En respuesta a la forma precisa Thread.Sleep es, no lo es en absoluto exacto. Creo que la resolución está en algún lugar alrededor de 10 ms. No está garantizado hacer mucho de nada excepto 'aproximadamente' este tiempo.

+0

¿Por qué no simplemente Thread.Sleep (milisegundos + 100)? – MusiGenesis

+0

¿Tiene alguna referencia que pueda indicarme acerca de la resolución? Si es definitivamente 10 ms podría cambiar 2999 a 2990 ..... – mezoid

+1

No hay una resolución rápida debido a cosas que otras personas han notado (como la carga del procesador que lo influencia). Básicamente se trata de que cada vez que su hilo tenga prioridad nuevamente, comience a ejecutarse. La evidencia anecdótica que he visto alrededor de SO en varios lugares, aunque ha apuntado a un promedio de alrededor de 10 ms. –

2

El hilo de dormir y el tiempo/regulación son cosas muy diferentes, y deben tratarse adecuadamente. Dormir un hilo es una tarea general, que permite que el sistema otorgue a otros hilos y procesos la posibilidad de ejecutar sin ser específico al respecto. Por otro lado, el estrangulamiento de una aplicación o las tareas de programación que requieren una sincronización precisa se deben realizar con un temporizador explícito.

Tenga en cuenta que si necesita procesos o sincronización precisos en el tiempo, tendrá dificultades para lograr eso con un proceso normal en Windows. Tendrá que utilizar las prioridades de Windows en tiempo real para lograr con éxito el tiempo exacto o la aceleración, ya que Windows puede suspender cualquier hilo en cualquier momento si es reemplazado por otro hilo.

+0

Acepto, la intención de Thread.Sleep es acerca de las prioridades del hilo y no de la sincronización exacta del reloj. Hasta que podamos tener un reloj de procesamiento Cesium en nuestras PC, lamentablemente el tiempo preciso (absoluto) no estará disponible. : P http://en.wikipedia.org/wiki/Caesium – Russell

+0

Bueno, espero que la mayoría de las personas no necesiten un tiempo preciso para el reloj atómico. : P Creo que el reloj en la mayoría de las CPU es lo suficientemente preciso para la mayoría de las aplicaciones más allá de la medición de las vibraciones atómicas. :RE – jrista

2

experimentar rápidamente, noto que un fragmento de código como ...

hacer {Debug.WriteLine (DateTime.Now.TimeOfDay.TotalMilliseconds.Encadenar()); } while (1);

muestra el mismo número varias veces, luego salta a un nuevo número muestran varias veces, etc. La brecha entre estos conjuntos de números es consistentemente 15.625ms que noto es 1000/64.

Parece que los temporizadores de Windows tiene una granularidad de 1/64 de segundo. Si necesitas algo mejor que eso, entonces siento tu dolor, pero ese es el marco en el que tienes que encajar. (Windows no es un sistema operativo duro en tiempo real y no pretende serlo).

0

Tal vez no confíe en el sesgo de tiempo para averiguar si tuvo éxito. Mejor contar su número de intentos y evaluar contra esta:

int tries; 

for(tries=0; tries<3; tries++) 
{ 
    Thread.Sleep(oneSecond); 
} 

Assert.GreaterOrEqual(tries, 3);