Ошибки измерения прошедшего времени

1

Наткнулся на неожиданное поведение во время измерений времени.

Я начал с класса 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 если изменение дня между измерениями является для вас случаем.

  • 0
    Thread.Sleep общеизвестно неточен, смотрите stackoverflow.com/questions/12321225/…
  • 0
    Thread.Sleep Я изначально использовал System.Threading.Timer , который показал те же результаты, поэтому я решил использовать Thread.Sleep в примерах для экономии места
Показать ещё 7 комментариев
Теги:
datetime
stopwatch

2 ответа

1
Лучший ответ

Но выяснилось, что время от времени будет меньше 200 мс.

Я не могу воспроизвести ваши результаты (ни локально, ни со ссылкой на dotnetfiddle.net), но в любом случае Thread.Sleep не выглядит точным. См. Например, насколько точна Thread.Sleep(TimeSpan)? ,

Может быть, немного не по теме, но для функции WINAPI Sleep MSDN говорит (акцент мой):

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


То, что так просто, как правило, возвращает отрицательные результаты.

DateTime.Now.Millisecond не возвращает всего миллисекунды, всего миллисекундный компонент.

  • 0
    Правда мой плохой. Это может сойти с доски
2

Объявление 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 нс интервалы), которые прошли "с начала времени".

  • 0
    как я уже сказал, Thread.Sleep существует только для экономии места в вопросе. Изначально это был System.Theading.Timer. Знаете ли вы, Timer страдает той же проблемой, что и Thread.Sleep?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню