¿Cuán exacto es Thread.Sleep (TimeSpan)?

Me encontré con una prueba de unidad que está fallando intermitentemente porque el tiempo transcurrido no es lo que espero que sea.

Un ejemplo de cómo se ve esta prueba 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 fallado al menos en una ocasión porque no:

Esperado: mayor que 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?

Solo como 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, quiero volver a intentar llamarlo hasta un máximo de 3 veces, pero esperar 1 segundo entre cada bash. El objective de la prueba unitaria, en este caso, es verificar que cuando solicitamos 3 bashs con 1 segundo de espera entre cada rebash, el tiempo total requerido es mayor a 3 segundos.

    Su hilo está compartiendo 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 del proceso, el número de subprocesos concurrentes, incluso de otros procesos, tendrán efecto sobre él.

    Thread.Sleep no está destinado a usarse para despertar con precisión. Realmente, la architecture de ventanas en sí misma no está pensada para este tipo de cosas.

    Experimentando rápidamente, noto que un fragmento de código como …

    do {Debug.WriteLine (DateTime.Now.TimeOfDay.TotalMilliseconds.ToString ()); } while (1);

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

    Parece que los temporizadores de Windows tienen 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).

    El hilo de dormir y el tiempo / regulación son cosas muy diferentes y deben tratarse de manera adecuada. 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 progtwigció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 lograrlo 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.

    En una aplicación en la que quería dormir durante al menos x milisegundos, utilicé un código similar al siguiente:

     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 precisión de Thread.Sleep, no es preciso en absoluto. 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.

    Tal vez no deberías confiar en el sesgo de tiempo para averiguar si fue exitoso. Mejor cuente su número de bashs y evalúe en contra de esto:

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