Любопытно, что ваш код отлично работает на моем четырехъядерном ядре под Win7, генерируя значения с интервалом в 2 мс почти каждый раз.
Итак, я сделал более тщательный тест. Вот мой пример вывода для Thread.Sleep(1)
. Код выводит количество мс между последовательными вызовами в DateTime.UtcNow
в цикле:
Каждая строка содержит 100 символов и, таким образом, представляет 100 мс времени в «чистом цикле». Таким образом, этот экран занимает примерно 2 секунды. Самый длинный приоритет был 4 мс; кроме того, был период продолжительностью около 1 секунды, когда каждая итерация занимала ровно 1 мс. Это качество ОС практически в реальном времени! 1 :)
Итак, я попытался снова, с Thread.Sleep(2)
на этот раз:
Опять почти идеальные результаты. На этот раз длина каждой строки составляет 200 мс, и есть пробег длиной почти 3 секунды, в котором промежуток никогда не превышал 2 мс.
Естественно, следующее, что нужно увидеть, это фактическое разрешение DateTime.UtcNow
на моей машине. Вот бег без сна вообще; .
печатается, если UtcNow
не изменилось вообще :
Наконец, при расследовании странного случая, когда временные метки находились на расстоянии 15 мс на той же машине, на которой были получены вышеуказанные результаты, я столкнулся со следующими любопытными случаями:
В Windows API есть функция под названием timeBeginPeriod
, которую приложения могут использовать для временного увеличения частоты таймера, так что это, вероятно, то, что здесь произошло. Подробная документация по разрешению таймера доступна через Hardware Dev Center Archive , в частности Timer-Resolution.docx (файл Word).
Выводы:
DateTime.UtcNow
может иметь гораздо более высокое разрешение, чем 15 мс
Thread.Sleep(1)
может спать ровно 1 мс
- На моей машине
UtcNow
растет с ростом ровно на 1 мс за раз (ошибка округления дает или принимает - рефлектор показывает, что в UtcNow
есть деление).
- Возможно переключение процесса в режим низкого разрешения, когда все основано на 15,6 мс, и режим высокого разрешения с кусочками 1 мс на лету.
Вот код:
static void Main(string[] args)
{
Console.BufferWidth = Console.WindowWidth = 100;
Console.WindowHeight = 20;
long lastticks = 0;
while (true)
{
long diff = DateTime.UtcNow.Ticks - lastticks;
if (diff == 0)
Console.Write(".");
else
switch (diff)
{
case 10000: case 10001: case 10002: Console.ForegroundColor=ConsoleColor.Red; Console.Write("1"); break;
case 20000: case 20001: case 20002: Console.ForegroundColor=ConsoleColor.Green; Console.Write("2"); break;
case 30000: case 30001: case 30002: Console.ForegroundColor=ConsoleColor.Yellow; Console.Write("3"); break;
default: Console.Write("[{0:0.###}]", diff / 10000.0); break;
}
Console.ForegroundColor = ConsoleColor.Gray;
lastticks += diff;
}
}
Оказывается, существует недокументированная функция, которая может изменить разрешение таймера. Я не исследовал детали, но я думал, что выложу ссылку здесь: NtSetTimerResolution
.
1 Конечно, я убедился, что операционная система максимально простаивает, и в ее распоряжении четыре достаточно мощных ядра процессора. Если я загружу все четыре ядра до 100%, картина полностью изменится, с длинными вытеснениями повсюду.