Наткнулся на неожиданное поведение во время измерений времени.
Я начал с класса Stopwatch
:
for (int i = 0; i < 100; i++)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
Thread.Sleep(200);
stopwatch.Stop();
Console.WriteLine(stopwatch.ElapsedMilliseconds);
}
https://dotnetfiddle.net/3hch1R
Но выяснилось, что время от времени будет меньше 200 мс.
Затем было время DateTime.Now
и это показало что-то странное:
for (int i = 0; i < 100; i++)
{
var start = DateTime.Now.Millisecond;
Thread.Sleep(200);
var stop = DateTime.Now.Millisecond;
Console.WriteLine(stop - start);
}
https://dotnetfiddle.net/2vo2yw
То, что так просто, как правило, возвращает отрицательные результаты.
Поэтому мой вопрос: в чем причина такого поведения для обоих классов и как я могу более точно измерить прошедшее время (без отрицательных отклонений)?
Есть сообщение о секундомерах, которые проливают свет на эту тему, но комментарии там выглядят немного противоречивыми.
Edit1:
Как заметил AlexD, DateTime.Now.Millisecond
просто возвращает миллисекунду, а не TotalMilliseconds. Итак, это ясно.
Edit2:
Следуя рекомендациям, я решил пойти с DateTime, это результаты удовлетворяет моим требованиям точности:
for (int i = 0; i < 100; i++)
{
var start = (int)DateTime.UtcNow.TimeOfDay.TotalMilliseconds;
Thread.Sleep(200);
var stop = (int)DateTime.UtcNow.TimeOfDay.TotalMilliseconds;
Console.WriteLine(stop - start);
}
Или DateTime.UtcNow.Ticks/TimeSpan.TicksPerMillisecond
если изменение дня между измерениями является для вас случаем.
Но выяснилось, что время от времени будет меньше 200 мс.
Я не могу воспроизвести ваши результаты (ни локально, ни со ссылкой на dotnetfiddle.net), но в любом случае Thread.Sleep
не выглядит точным. См. Например, насколько точна Thread.Sleep(TimeSpan)? ,
Может быть, немного не по теме, но для функции WINAPI Sleep
MSDN говорит (акцент мой):
Системные часы "гаснут" с постоянной скоростью. Если
dwMilliseconds
меньше разрешения системных часов, поток может спать меньше определенного периода времени. ЕслиdwMilliseconds
больше одного тика, но меньше двух, ожидание может быть где-то между одним и двумя тиками и так далее.
То, что так просто, как правило, возвращает отрицательные результаты.
DateTime.Now.Millisecond
не возвращает всего миллисекунды, всего миллисекундный компонент.
Объявление 1: Thread.Sleep - не очень точный метод для сна... Я предлагаю вам использовать комбинацию сна и ожидание: спящий, скажем, 180 мс, затем используйте ожидание, пока вы не достигнете 200 мс; это будет загружать CPU 100% за последние 20 мс (средняя загрузка ЦП на 10%, что может быть разумным), но даст вам более точное время выпуска.
Объявление 2: используйте DateTime.Ticks(http://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx), который возвращает число "тиков" (100 нс интервалы), которые прошли "с начала времени".
Thread.Sleep
Я изначально использовалSystem.Threading.Timer
, который показал те же результаты, поэтому я решил использоватьThread.Sleep
в примерах для экономии места