Как получить метку времени точности тика in.NET / C#?


до сих пор я использовал DateTime.Now для получения меток времени, но я заметил, что если вы печатаете DateTime.Now в цикле вы увидите, что он увеличивается в дискретных скачках прибл. Но для некоторых сценариев в моем приложении мне нужно получить максимально точную временную метку, предпочтительно с точностью тика (=100 нс). Есть идеи?

обновление:

видимо, StopWatch/QueryPerformanceCounter - это путь, но он может быть использован только для измерения времени, поэтому я был думая о вызове DateTime.Now когда приложение запускается, а затем просто есть StopWatch выполнить, а затем просто добавить прошедшее время от StopWatch к исходному значению, возвращенному из DateTime.Now. По крайней мере, это должно дать мне точные относительные временные метки, верно? Что вы думаете об этом (Хак)?

Примечание:

StopWatch.ElapsedTicks отличается от StopWatch.Elapsed.Ticks! Я использовал первый, предполагая, что 1 тик = 100 нс, но в этом случае 1 ТИК = 1/StopWatch.Frequency. Так клещей эквивалентно использованию DateTime StopWatch.Elapsed.Ticks. Я только что узнал это на собственном горьком опыте.

примечание 2:

Используя подход секундомера, я заметил, что он выходит из синхронизации с Реальным временем. Примерно через 10 часов он был впереди на 5 секунд. Поэтому я думаю, что нужно будет повторно синхронизировать его каждые X или около того, где X может быть 1 час, 30 мин, 15 мин и т. д. Я не уверен, что оптимальный промежуток времени для повторной синхронизации будет, так как каждая повторная синхронизация изменит смещение, которое может составлять до 20 мс.

10 59

10 ответов:

значение системных часов, что DateTime.Now чтение обновляется только каждые 15 мс или около того (или 10 мс в некоторых системах), поэтому время квантовано вокруг этих интервалов. Существует дополнительный эффект квантования, который возникает из-за того, что ваш код работает в многопоточной ОС, и, таким образом, есть участки, где ваше приложение не "живое" и, таким образом, не измеряет реальное текущее время.

так как вы ищете ультра-точное значение штампа времени (в отличие от просто синхронизации произвольной продолжительности),Stopwatch класс сам по себе не будет делать то, что вам нужно. Я думаю, что вам придется сделать это самостоятельно с помощью своего рода DateTime/Stopwatch гибрид. Когда ваше приложение запускается, вы бы сохранить текущий DateTime.UtcNow значение (т. е. время грубого разрешения при запуске приложения) , а затем также запустить

[принятый ответ не кажется потокобезопасным, и по его собственному признанию может идти назад во времени, вызывая дубликаты временных меток, следовательно, этот альтернативный ответ]

Если то, что вас действительно волнует (в вашем комментарии), на самом деле является уникальной меткой времени, которая выделяется в строгом порядке возрастания и которая как можно ближе соответствует системному времени, вы можете попробовать этот альтернативный подход:

public class HiResDateTime
{
   private static long lastTimeStamp = DateTime.UtcNow.Ticks;
   public static long UtcNowTicks
   {
       get
       {
           long orig, newval;
           do
           {
               orig = lastTimeStamp;
               long now = DateTime.UtcNow.Ticks;
               newval = Math.Max(now, orig + 1);
           } while (Interlocked.CompareExchange
                        (ref lastTimeStamp, newval, orig) != orig);

           return newval;
       }
   }
}

чтобы получить тиковое число с высоким разрешением, пожалуйста, используйте статический секундомер.GetTimestamp()-метод:

long tickCount = System.Diagnostics.Stopwatch.GetTimestamp();
DateTime highResDateTime = new DateTime(tickCount);

просто взгляните на исходный код .NET:

    public static long GetTimestamp() {
        if(IsHighResolution) {
            long timestamp = 0;    
            SafeNativeMethods.QueryPerformanceCounter(out timestamp);
            return timestamp;
        }
        else {
            return DateTime.UtcNow.Ticks;
        }   
    }

исходный код здесь: http://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Stopwatch.cs,69c6c3137e12dab4

эти предложения все выглядят слишком трудно! Если вы работаете в Windows 8 или Server 2012 или выше, используйте GetSystemTimePreciseAsFileTime следующим образом:

[DllImport("Kernel32.dll", CallingConvention = CallingConvention.Winapi)]
static extern void GetSystemTimePreciseAsFileTime(out long filetime);

public DateTimeOffset GetNow()
{
    long fileTime;
    GetSystemTimePreciseAsFileTime(out fileTime);
    return DateTimeOffset.FromFileTime(fileTime);
}

Это гораздо лучше, чем DateTime.Now без каких-либо усилий.

см. MSDN для получения дополнительной информации: http://msdn.microsoft.com/en-us/library/windows/desktop/hh706895(v=vs.85).aspx

возвращает наиболее точную дату и время в операционной системе.

операционная система также обеспечивает более высокое временное разрешение через QueryPerformanceCounter и QueryPerformanceFrequency (.NET Stopwatch класс). Они позволяют вам время интервал, но не дают вам дату и время суток. Вы можете возразить, что они могли бы дать вам очень точное время и день, но я не уверен, насколько сильно они искажаются в течение длительного интервала.

1). Если вам нужно высокое разрешение абсолютная точность: вы не можете использовать DateTime.Now когда он основан на часах с интервалом 15 мс (если это не так можно "сдвинуть" фазу).

вместо этого, внешний источник лучшего абсолютного разрешения время точности (например, ntp),t1 ниже, смогло быть совмещено с максимумом таймер разрешения (секундомер / QueryPerformanceCounter).


2). Если вам просто нужно высокое разрешение:

пример DateTime.Now (t1) один раз вместе со значением из таймер высокого разрешения (секундомер / QueryPerformanceCounter) (tt0).

если текущее значение таймера высокого разрешения tt затем текущее время, t, является:

t = t1 + (tt - tt0)

3). Альтернативой может быть распутывание абсолютного времени и порядок финансовых событий: одно значение для абсолютного времени (Разрешение 15 мс, возможно отключение на несколько минут) и один стоимость заказа (например, увеличение значения на единицу каждый время и магазин). Начальное значение для заказа может быть основано на некотором значении системы глобальном или выведено от абсолютное время запуска приложения.

это решение было бы более надежным, поскольку оно не зависит от базовая аппаратная реализация часов / таймеров (это может варьироваться между системами).

Это слишком много работы для чего-то настолько простого. Просто вставьте DateTime в свою базу данных с торговлей. Затем для получения торгового ордера используйте свой столбец идентификаторов, который должен быть увеличивающимся значением.

Если вы вставляете в несколько баз данных и пытаетесь согласовать после этого факта, то вы будете неправильно вычислять торговый ордер из-за любого небольшого отклонения в вашем времени базы данных (даже приращения ns, как вы сказали)

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

даже используя одно из решений выше, любой, кто проводит сделки из места в сети с большей задержкой, может сначала разместить сделки (в реальном мире), но они вводятся в неправильном порядке из-за задержки. Из-за этого торговля должна считаться размещенной в базе данных, а не на консолях пользователей.

точность 15 МС (на самом деле это может быть 15-25 МС) основана на разрешении таймера Windows 55 Гц/65 Гц. Это также основной период TimeSlice. Пострадавшие находятся Windows.Формы.Таймер,нарезание резьбы.Нитка.Спи,нарезание резьбы.Таймер и т. д.

чтобы получить точные интервалы, вы должны использовать секундомер класс. Он будет использовать высокое разрешение, если таковое имеется. Попробуйте следующие инструкции, чтобы узнать:

Console.WriteLine("H = {0}", System.Diagnostics.Stopwatch.IsHighResolution);
Console.WriteLine("F = {0}", System.Diagnostics.Stopwatch.Frequency);
Console.WriteLine("R = {0}", 1.0 /System.Diagnostics.Stopwatch.Frequency);

Я вам R=6E-08 сек, или 60 НС. Этого должно быть достаточно для вашей цели.

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

Я бы добавил следующее относительно MusiGenesis ответ повторной синхронизации.

смысл: какое время я должен использовать для повторной синхронизации (_maxIdle на MusiGenesis ответ)

вы знаете, что с этим решением вы не совсем точны, вот почему вы повторно синхронизируете. Но и то, что вы неявно хотите, это то же самое, что Иэн Мерсер решение:

уникальный момент выделено в строгом порядке возрастания

поэтому количество времени между двумя повторными синхронизациями (_maxIdle назовем это SyncTime) должна быть функция 4 вещи:

  • the DateTime.UtcNow разрешение
  • соотношение точности вы хотите
  • уровень точности вы хотите
  • оценка коэффициента несинхронизации вашей машины

очевидно, что первое ограничение на эту переменную будет быть :

из-за отсутствия синхронизации соотношение

например: я не хочу, чтобы моя точность была хуже, чем 0,5 с/ч или 1 мс/день и т. д... (на плохом английском языке: я не хочу быть более неправильным, чем 0.5 s/hrs=12s/day).

таким образом, вы не можете достичь лучшей точности, чем то, что секундомер предлагает вам на вашем ПК. Это зависит от вашего коэффициента несинхронизации, который может быть не постоянным.

еще одним ограничением является минимальное время между двумя повторная синхронизация:

Synctime >= DateTime.UtcNow разрешение

здесь точность и точность связаны, потому что если вы используете высокую точность (например, для хранения в БД), но более низкую точность, вы можете сломать заявление Яна Мерсера это строгий порядок возрастания.

Примечание: кажется, дата и время.UtcNow может иметь большее значение по умолчанию, чем 15 мс (1 мс на моей машине) перейдите по ссылке: высокая точность значение datetime.UtcNow

рассмотрим пример:

представьте себе коэффициент несинхронизации, прокомментированный выше.

после 10 часов, он был впереди на 5 секунд.

скажем, я хочу точность микросекунд. Мой таймер res составляет 1 мс (см. выше Примечание)

Итак, по пунктам:

  • the : 1ms
  • коэффициент точности >= Коэффициент несинхронизации, давайте возьмите наиболее точный из возможных так:коэффициент точности = Коэффициент несинхронизации
  • уровень точности вы хотите:1 мкс
  • оценка коэффициента несинхронизации вашей машины:0,5 с/час (это тоже моя точность)

если вы сбрасываете каждые 10 секунд, представьте себе, что вы в 9.999 s, 1ms перед сбросом. Здесь вы делаете вызов в течение этого интервала. Время, в течение которого ваша функция будет строить график, опережает : 0.5/3600*9.999 s eq 1.39 МС. Вы бы отображали время 10.000390 сек. после utcnow tick, если вы сделаете вызов в течение 390micro сек, ваш номер будет уступать предыдущему. Его хуже, если этот коэффициент несинхронизации является случайным в зависимости от загрузки процессора или других вещей.

теперь допустим, я ставлю SyncTime при его минимальном значении > я повторно синхронизирую каждые 1 мс

делая то же самое мышление поставит меня впереди времени на 0,139 микросекунды

Итак, здесь другое ограничение: точность-отношение X SyncTime коэффициент точности X SyncTime - это хорошо.

проблема решена.

так что быстрый обзор будет :

  • получить разрешение таймера.
  • вычислить оценку из-за отсутствия синхронизации соотношение.
  • коэффициент точности >= оценка коэффициента несинхронизации,лучшая точность = коэффициент несинхронизации
  • выберите свой уровень точности, учитывая следующее:
  • timer-resolution
  • самая лучшая точность вы можете достигнуть таймер-разрешение * 2 * коэффициент несинхронизации

для вышеуказанного соотношения (0,5 / час) правильное время синхронизации будет 3,6 МС, поэтому округляется до 3ms.

С выше соотношение и разрешение таймера 1 мс. Если вы хотите уровень точности один тик (0.1 микросекунды), вам нужно вне синхронизации соотношение не более : звука 180 МС/час.

в своем последнем ответе на свой собственный ответ MusiGenesis состояние:

@Hermann: я выполнял аналогичный тест в течение последних двух часов (без коррекции сброса), а таймер на основе секундомера работает только на 400 мс вперед через 2 часа, поэтому сам перекос кажется переменным (но все еще довольно серьезным). Я очень удивлен, что перекос настолько плох; я думаю, именно поэтому секундомер находится в системе.Диагностика. – MusiGenesis

так точность секундомера близко к 200мс / Хоур, почти нашему 180мс / час. Есть ли связь с тем, почему наш номер и этот номер так близки ? Не знаю. Но этой точности нам достаточно для достижения тиковой точности

лучший уровень точности: для примера выше это 0.27 МКС.

однако что произойдет, если я назову его несколько раз между 9.999 МС и повторной синхронизацией.

2 вызова функции может в конечном итоге с той же меткой времени возвращается время будет 9.999 для обоих (как я не смотрите дополнительную точность). Чтобы обойти это, вы не можете коснуться уровня точности, потому что он связан с SyncTime приведенным выше отношением. Так что вы должны реализовать Иэн Мерсер решение для тех случае.

пожалуйста, не стесняйтесь комментировать мой ответ.