Насколько точна Thread.Sleep(TimeSpan)?
Я столкнулся с unit test, который прерывается с перерывами, потому что прошедшее время не то, что я ожидаю.
Пример того, как выглядит этот тест:
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 секунду между каждая попытка. Цель unit test в этом случае состоит в том, чтобы проверить, что, когда мы запросили 3 повтора с 1 секундой ожидания между каждой попыткой, общее время, затраченное на большее, чем 3 секунды.
Ответы
Ответ 1
Ваша тема разделяет время процессора с другими потоками. Сон закончится, как только вы вернетесь, и ядро замечает, что время сна истекло, поэтому это не так точно.
Нагрузка на CPU, приоритеты процессов, количество параллельных потоков, даже из других процессов, будут влиять на нее.
Ответ 2
Thread.Sleep не предназначен для точного бодрствования. Действительно, сама архитектура окон не предназначена для такого рода вещей.
Ответ 3
Снабжение нити и выбор времени/дросселирование - это очень разные вещи, и к ним следует относиться надлежащим образом. Спящий поток - общая задача, позволяющая системе давать другим потокам и процессам возможность выполнить, не будучи конкретными. С другой стороны, регулирование приложения или планирование задач, требующих точного времени, должно выполняться с явным таймером.
Имейте в виду, что если вам понадобятся временные процессы или синхронизация, вам будет трудно достичь этого с помощью обычного процесса в окнах. Вам нужно будет использовать приоритеты в реальном времени для Windows, чтобы успешно достичь точного времени или дросселирования, поскольку окна могут скрыть любой поток в любое время, если он выгружен другим потоком.
Ответ 4
Быстро экспериментируя, я замечаю, что фрагмент кода, например...
do {Debug.WriteLine(DateTime.Now.TimeOfDay.TotalMilliseconds.ToString()); } while (1);
отображает одно и то же число несколько раз, затем перескакивает на новый номер, отображаемый несколько раз и т.д. Разрыв между этими наборами чисел составляет 15,625 мс, который я замечаю 1000/64.
Похоже, что таймеры Windows имеют степень детализации 1/64 секунды. Если вам нужно лучше, чем тогда, я почувствую вашу боль, но это рамки, в которые вы должны вписаться. (Windows не является жесткой оперативной ОС и не претендует на это).
Ответ 5
В одном приложении, где я хотел спать по меньшей мере за миллисекунды, я использовал некоторый код, похожий на:
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 мс. Не гарантируется выполнение большей части всего, кроме "приблизительно" этого долгого времени.
Ответ 6
Возможно, вы не полагаетесь на временную предвзятость, чтобы узнать, успешно ли вы. Лучше подсчитайте количество попыток и оцените это:
int tries;
for(tries=0; tries<3; tries++)
{
Thread.Sleep(oneSecond);
}
Assert.GreaterOrEqual(tries, 3);