Насколько точен Thread.Sleep (TimeSpan)? - PullRequest
16 голосов
/ 20 августа 2009

Я столкнулся с модульным тестом, который периодически прерывается, потому что прошедшее время не соответствует ожидаемому.

Пример того, как выглядит этот тест:

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);

В большинстве случаев это проходит, но по крайней мере в одном случае это не удалось, потому что:

Ожидается: больше или равно 2999 Но было: 2998

Я не понимаю, как это может быть меньше 3 секунд. Есть ли проблема точности с Thread.Sleep или, возможно, секундомер, о котором я не знаю?

Так же, как обновление некоторых вопросов ниже. Сценарий, который подвергается модульному тестированию, - это класс, позволяющий вызывать метод для выполнения какого-либо действия, а в случае сбоя подождите секунду и вызовите этот метод. Тест, показанный выше, является лишь приблизительным примером того, что происходит.

Скажем, я хотел вызвать метод DoSomething () ... но в случае возникновения исключения с помощью DoSomething () я хочу иметь возможность повторить вызов не более 3 раз, но ждать 1 секунду между каждая попытка. Цель модульного теста, в данном случае, состоит в том, чтобы убедиться, что, когда мы запросили 3 повторных попытки с интервалом в 1 секунду между каждой повторной попыткой, общее время, которое требуется, превышает 3 секунды.

Ответы [ 6 ]

15 голосов
/ 20 августа 2009

Ваша тема делит процессорное время с другими потоками. Сон закончится, как только наступит ваш черед, и ядро ​​заметит, что время сна истекло, поэтому оно не настолько точное.

На него будут влиять загрузка процессора, приоритеты процессов, количество параллельных потоков, даже из других процессов.

8 голосов
/ 20 августа 2009

Thread.Sleep не предназначен для точного пробуждения. Действительно, сама архитектура Windows не предназначена для такого рода вещей.

3 голосов
/ 15 декабря 2009

Быстро экспериментируя, я замечаю, что фрагмент кода похож на ...

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

отображает один и тот же номер несколько раз, затем переходит к новому номеру, отображаемому несколько раз, и т. Д. Разрыв между этими наборами чисел составляет 15.625 мс, который, как я заметил, составляет 1000/64.

Похоже, что таймеры Windows имеют гранулярность 1/64 секунды. Если вам нужно лучше, тогда я чувствую вашу боль, но это рамки, в которые вы должны вписаться. (Windows не является жесткой ОС реального времени и не претендует на это).

2 голосов
/ 20 августа 2009

Спящий поток и синхронизация / регулирование - это очень разные вещи, и к ним следует относиться соответствующим образом. Спящий поток - это общая задача, позволяющая системе предоставить другим потокам и процессам возможность выполнения, не обращая на это особого внимания. С другой стороны, регулирование приложения или планирование задач, которые требуют точной синхронизации, должны выполняться с явным таймером.

Имейте в виду, что если вам нужны процессы или синхронизация с точным временем, вам будет сложно добиться этого с помощью обычного процесса в Windows. Вам потребуется использовать приоритеты Windows в реальном времени для успешного достижения точного времени или регулирования, так как окна могут в любой момент перевести любой поток в режим ожидания, если он прерван другим потоком.

1 голос
/ 20 августа 2009

В одном приложении, где я хотел спать не менее x миллисекунд, я использовал код, подобный следующему:

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();
}

В ответ на то, насколько точен Thread.Sleep, он совсем не точен. Я думаю, что разрешение составляет около 10 мс. Не гарантировано делать что-либо, кроме «примерно» этой длины.

0 голосов
/ 15 декабря 2009

Может быть, вы не полагаетесь на смещение времени, чтобы узнать, где вы успешно. Лучше посчитайте количество попыток и сравните с этим:

int tries;

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

Assert.GreaterOrEqual(tries, 3);
...