Как часто обновляется DateTime.Now? или есть более точный API, чтобы получить текущее время?
У меня есть код, запущенный в цикле, и он сохраняет состояние на основе текущего времени. Иногда это может быть всего лишь в миллисекундах, но по какой-то причине кажется, что DateTime.Now всегда будет возвращать значения не менее 10 мс друг от друга, даже если это будет только через 2 или 3 мс позже. Это представляет серьезную проблему, так как состояние, которое я сохраняю, зависит от времени его сохранения (например, что-то записывает)
Мой тестовый код, который возвращает каждое значение на расстоянии 10 мс:
public static void Main()
{
var dt1 = DateTime.Now;
System.Threading.Thread.Sleep(2);
var dt2 = DateTime.Now;
// On my machine the values will be at least 10 ms apart
Console.WriteLine("First: {0}, Second: {1}", dt1.Millisecond, dt2.Millisecond);
}
Есть ли еще одно решение о том, как получить точное текущее время до миллисекунды?
Кто-то предложил посмотреть класс Секундомера. Хотя класс секундомера очень точен, он не говорит мне о текущем времени, что мне нужно, чтобы сохранить состояние моей программы.
Ответы
Ответ 1
Любопытно, что ваш код отлично работает на моем четырехъядерном ядре под Win7, генерируя значения ровно 2 мс друг от друга почти каждый раз.
Итак, я сделал более тщательный тест. Здесь мой пример выводится для Thread.Sleep(1)
. Код печатает количество мс между последовательными вызовами в DateTime.UtcNow
в цикле:
![sleep 1]()
Каждая строка содержит 100 символов и, таким образом, составляет 100 мс времени при "чистом прогоне". Таким образом, этот экран занимает примерно 2 секунды. Самое длинное предположение - 4 мс; Более того, период длился около 1 секунды, когда каждая итерация составляла ровно 1 мс. Это почти оперативное качество ОС! 1:)
Итак, я попробовал еще раз: Thread.Sleep(2)
на этот раз:
![sleep 2]()
Опять же, почти идеальные результаты. На этот раз каждая строка длится 200 мс, и там работает почти 3 секунды, где разрыв никогда не был чем-то иным, чем ровным 2 мс.
Естественно, следующее, что нужно увидеть, - это фактическое разрешение DateTime.UtcNow
на моей машине. Здесь бег без сна вообще; a .
печатается, если UtcNow
не изменилось вообще:
![no sleep]()
Наконец, при изучении странного случая временных меток на расстоянии 15 мс на той же машине, которая произвела вышеупомянутые результаты, я столкнулся с следующими любопытными событиями:
![enter image description here]()
![enter image description here]()
В Windows API есть функция, называемая timeBeginPeriod
, какие приложения могут использовать для временного увеличения частоты таймера, поэтому это предположительно что здесь случилось. Подробную документацию по разрешению таймера можно получить через Hardware Dev Center Archive, в частности Timer-Resolution.docx (файл Word).
Выводы:
-
DateTime.UtcNow
может иметь гораздо более высокое разрешение, чем 15 мс
-
Thread.Sleep(1)
может спать ровно 1 мс
- На моей машине
UtcNow
растет на ровно 1 мс за раз (дайте или возьмите ошибку округления - Reflector показывает, что существует деление в 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%, изображение полностью изменится с длинными превенциями во всем мире.
Ответ 2
Проблема с DateTime при работе с миллисекундами вообще не связана с классом DateTime, а скорее связана с тиками ЦП и срезами потоков. По сути, когда операция приостанавливается планировщиком, чтобы разрешить выполнение других потоков, она должна подождать как минимум 1 временной отрезок перед возобновлением, который составляет около 15 мс в современных ОС Windows. Поэтому любая попытка сделать паузу менее чем на 15 мс приведет к неожиданным результатам.
Ответ 3
Если вы сделаете мгновенный снимок текущего времени, прежде чем что-либо сделать, вы можете просто добавить секундомер к моменту сохранения, не?
Ответ 4
Вы должны спросить себя, действительно ли вам нужно точное время или просто достаточно близкое время и увеличивающееся целое число.
Вы можете делать хорошие вещи, получая сейчас() сразу после события ожидания, такого как мьютекс, выбор, опрос, WaitFor * и т.д., а затем добавление к нему серийного номера, возможно, в наносекундном диапазоне или там, где есть номер.
Вы также можете использовать командную инструкцию rdtsc (для некоторых библиотек для этого есть обертка API, не уверенная в том, что вы делаете это на С# или Java), чтобы получить дешевое время от процессора и объединить это с течением времени(). Проблема с rdtsc заключается в том, что в системах со скоростным масштабированием вы никогда не сможете быть уверенными, что это будет делать. Он также довольно быстро обертывается.
Ответ 5
Все, что я использовал для выполнения этой задачи на 100%, - это таймер и метка.
Код не требует большого объяснения, довольно просто.
Глобальные переменные:
int timer = 0;
Это событие галочки:
private void timeOfDay_Tick(object sender, EventArgs e)
{
timeOfDay.Enabled = false;
timer++;
if (timer <= 1)
{
timeOfDay.Interval = 1000;
timeOfDay.Enabled = true;
lblTime.Text = "Time: " + DateTime.Now.ToString("h:mm:ss tt");
timer = 0;
}
}
Здесь загружается форма:
private void DriverAssignment_Load(object sender, EventArgs e)
{
timeOfDay.Interval= 1;
timeOfDay.Enabled = true;
}
Ответ 6
Отвечая на вторую часть вашего вопроса относительно более точного API, комментарий от AnotherUser приведет меня к этому решению, которое в моем сценарии преодолевает проблема точности DateTime.Now:
static FileTime time;
public static DateTime Now()
{
GetSystemTimePreciseAsFileTime(out time);
var newTime = (ulong)time.dwHighDateTime << (8 * 4) | time.dwLowDateTime;
var newTimeSigned = Convert.ToInt64(newTime);
return new DateTime(newTimeSigned).AddYears(1600).ToLocalTime();
}
public struct FileTime
{
public uint dwLowDateTime;
public uint dwHighDateTime;
}
[DllImport("Kernel32.dll")]
public static extern void GetSystemTimePreciseAsFileTime(out FileTime lpSystemTimeAsFileTime);
В моих собственных тестах, повторяя 1M, он возвращается в среднем по 3 тика против DateTime.Now 2 ticks.
Почему 1600 из моей юрисдикции, но я использую его, чтобы получить правильный год.
EDIT: Это все еще проблема на win10. Любой, кто заинтересован, может справиться с этим свидетельством:
void Main()
{
for (int i = 0; i < 100; i++)
{
Console.WriteLine(Now().ToString("yyyy-MM-dd HH:mm:ss.fffffff"));
Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffffff"));
Console.WriteLine();
}
}
// include the code above
Ответ 7
Вы можете использовать DateTime.Now.Ticks, прочитать текст на MSDN
"Один тик представляет собой сто наносекунд или одну десятую миллионную долю секунды".