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

До сих пор я использовал DateTime.Now для получения меток времени, но я заметил, что если вы напечатаете DateTime.Now в цикле, вы увидите, что он увеличивается с дискретными скачками примерно на. 15 мс. Но для определенных сценариев в моем приложении мне нужно получить максимально точную временную метку, желательно с точностью до тика (= 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:

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


person Community    schedule 12.09.2009    source источник
comment
Конечно, дата и время, установленные на сервере, не гарантируют точность до 100 нс, поэтому я не уверен, какой смысл иметь точную метку времени (поскольку это точность будет только с сервером)?   -  person Dan Diplo    schedule 13.09.2009
comment
Мне нужны финансовые данные в реальном времени, когда точность очень важна.   -  person    schedule 13.09.2009
comment
Очевидно, мне нравится твой хак. :) Но почему финансовые данные должны быть с точностью до 100 нс? Я могу понять это, если вы отмечаете данные, записанные в базу данных, и вам нужно различать строки, которые в противном случае имели бы точно такие же значения временных меток.   -  person MusiGenesis    schedule 13.09.2009
comment
Я выполняю расчеты там, где требуется такая точность. Также кажется неправильным видеть временные метки сделок, которые происходят последовательно с одной и той же временной меткой, хотя на самом деле вы знаете, что они не произошли в одно и то же время.   -  person    schedule 13.09.2009


Ответы (10)


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

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

DateTime _starttime = DateTime.UtcNow;
Stopwatch _stopwatch = Stopwatch.StartNew();

Затем, когда вам понадобится значение DateTime с высоким разрешением, вы получите его так:

DateTime highresDT = _starttime.AddTicks(_stopwatch.Elapsed.Ticks);

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

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

public class HiResDateTime
{
    private static DateTime _startTime;
    private static Stopwatch _stopWatch = null;
    private static TimeSpan _maxIdle = 
        TimeSpan.FromSeconds(10);

    public static DateTime UtcNow
    {
        get
        {
            if ((_stopWatch == null) || 
                (_startTime.Add(_maxIdle) < DateTime.UtcNow))
            {
                Reset();
            }
            return _startTime.AddTicks(_stopWatch.Elapsed.Ticks);
        }
    }

    private static void Reset()
    {
        _startTime = DateTime.UtcNow;
        _stopWatch = Stopwatch.StartNew();
    }
}

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

person MusiGenesis    schedule 12.09.2009
comment
Да, это то, что я придумал, когда прочитал другие отзывы о StopWatch / QueryPerformanceCounter. Благодарим за предоставленный образец кода. Я уверен, что это принесет пользу и другим. - person ; 13.09.2009
comment
Кстати, использование DateTime.UtcNow вместо DateTime.Now действительно важно для создания меток времени. В противном случае переход на летнее время будет утомлять вас каждую осень, внезапно отбрасывая значения меток времени на час назад. - person MusiGenesis; 13.09.2009
comment
Спасибо. Мне уже пришлось реализовать свои собственные классы Date, Time и DateAndTime, чтобы обойти все недостатки DateTime (например, отсутствие встроенной поддержки часовых поясов). Кажется чрезмерным изобретать велосипед в отношении даты и времени, но я действительно удивлен, насколько плохой DateTime был спроектирован и реализован. Можно подумать, что, обладая всеми ресурсами и знаниями, они могли бы добиться большего. - person ; 13.09.2009
comment
‹Да, это то, что я придумал, когда прочитал› Я бы не стал недооценивать свой предыдущий комментарий о перекосе. Особенно, когда в документации по QueryPerformanceCounter говорится об ошибках в BIOS и HAL. :) - person Jason Kresowaty; 13.09.2009
comment
@binary: как я сказал в своем ответе, я не совсем уверен, что мой гибрид DateTime / Stopwatch действительно будет рассинхронизирован с обычным DateTime, поскольку я думаю в конечном итоге они движутся одним и тем же основным механизмом. DateTime квантуется до 15 мс, потому что именно так часто обновляется системное время, но основным механизмом, выполняющим обновление, является тот же механизм, что и секундомер ‹/THEORY›. - person MusiGenesis; 13.09.2009
comment
@Hermann: вы знаете о классе System.TimeZone? (только что открыл это сам) - person MusiGenesis; 13.09.2009
comment
@MusiGenesis: я использую класс TimeZoneInfo для внутренних целей. MSDN предлагает использовать это вместо TimeZone. TimeZoneInfo поддерживает преобразование во всех часовых поясах, в то время как TimeZone поддерживает только Local и UTC (ничем не отличается от DateTime). - person ; 16.09.2009
comment
@MusiGenesis: я только что обнаружил ошибку в своем коде (и в вашем примере кода). См. Мое ПРИМЕЧАНИЕ в моем обновленном вопросе. Возможно, вы захотите обновить образец кода. Спасибо. - person ; 16.09.2009
comment
@binarycoder: Я выясню, не рассинхронизируется ли оно (мое приложение будет работать не менее 10 часов), и опубликую здесь. - person ; 16.09.2009
comment
@Hermann: спасибо, я отредактировал свой код. Я должен был упомянуть в своем первоначальном ответе, что этот код не тестировался. - person MusiGenesis; 16.09.2009
comment
@binarycoder: Вы были правы. Это действительно рассинхронизируется. Я заметил, что после 10 часов он был примерно на 5 секунд впереди. Думаю, мне придется повторно синхронизировать его каждые X или около того, я не уверен, что использовать для X. 1 час? 30 минут? 1 мин? - person ; 26.09.2009
comment
@Hermann: это очень интересный результат. Похоже, это наводит на мысль, что секундомер, хотя и имеет очень высокое разрешение, не очень точен. - person MusiGenesis; 26.09.2009
comment
@Hermann: Я бы сбросил его всякий раз, когда он простаивал более заданного времени (см. Мое обновление). Я не знаю, является ли 10 секунд лучшим временем простоя, но я не думаю, что это помешает сбрасывать его так часто. - person MusiGenesis; 26.09.2009
comment
@Hermann: Я проводил аналогичный тест в течение последних двух часов (без коррекции сброса), и таймер на основе секундомера работает только примерно на 400 мс вперед через 2 часа, поэтому сам перекос кажется переменным (но все еще довольно суровый). Я очень удивлен, что перекос настолько плох; Думаю, поэтому Секундомер находится в System.Diagnostics. - person MusiGenesis; 26.09.2009
comment
Если вам нужна поточно-ориентированная версия, см. Ответ Яна Мерсера ниже. - person Frank Hileman; 19.04.2013
comment
@FrankHileman: если читатели понимают, что ответ Яна имеет точность, ограниченную 16 мс по Datetime. Было бы лучше изменить этот ответ, чтобы он был потокобезопасным. - person ToolmakerSteve; 24.09.2015
comment
Интересным усовершенствованием было бы непрерывное изменение. В данной системе отслеживайте перекос секундомера и даты и времени. Поэтому вместо того, чтобы делать резкие скачки для сброса после периода простоя, отрегулируйте каждое возвращаемое значение на основе множителя (который будет немного выше или ниже 1, в зависимости от дрейфа системы; например, 1.0000001). Вместо выполнения сброса после простоя, который может вызвать скачок времени до 16 мс, периодически опрашивайте datetime, каждый раз делая микроскопические изменения в множителе (и устанавливайте временное смещение, чтобы новый множитель применялся только ко времени после изменять). - person ToolmakerSteve; 24.09.2015
comment
Или, как альтернатива, пусть отдельный поток периодически выполняет сброс. Этот другой поток должен большую часть времени находиться в спящем режиме, а затем довольно быстро просыпаться и опрашивать DateTime, чтобы узнать (точнее, чем до ближайших 16 мсек), когда DateTime изменится на новое значение. Когда он видит изменение DateTime, он считывает значение секундомера. Теперь у него есть соответствие между краем при переходе DateTime и временем секундомера. Так можно рассчитать точное смещение для будущих результатов времени. - person ToolmakerSteve; 24.09.2015
comment
@MusiGenesis - Ваш UtcNow ‹-› Неправильный подход к синхронизации секундомера. См. Эту ветку: stackoverflow.com/a/38127282/344638 - person antiduh; 01.07.2016
comment
Этот парень, кажется, исправил все проблемы с потоками в этом ответе и придумал еще более точные часы, nimaara.com/2016/07/06/high-resolution-clock-in-net - person MaYaN; 08.07.2016

Чтобы получить счетчик тиков с высоким разрешением, используйте статический метод Stopwatch.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

person Marcus.D    schedule 22.12.2014
comment
Спасибо за ваш комментарий. Я всегда пытаюсь найти кратчайший способ решения проблемы, используя доступные функции .NET framework. И поделитесь этим. - person Marcus.D; 24.12.2016
comment
Похоже, он не работает на .NET 4.5.2 и Win 10 x64. Дата имеет год 0001. - person Arek; 03.06.2019

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

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

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;
       }
   }
}
person Ian Mercer    schedule 16.01.2013
comment
+1. См. stackoverflow.com/questions/5608980/ для моих мыслей. - person Lee Grissom; 04.04.2013
comment
Для протокола, я не признавал, что мой метод может идти назад во времени. Я писал: если вы сбрасываете гибридный таймер через некоторый регулярный интервал (скажем, каждый час или что-то в этом роде), вы рискуете вернуть время назад до времени последнего чтения, что было объяснением того, почему я выбрал not для сброса таймера через некоторый регулярный интервал. Однако мое решение определенно не было потокобезопасным, и мне нравится функция предотвращения дублирования в вашем решении. - person MusiGenesis; 25.06.2014
comment
Ограничением этого решения является то, что его разрешение ограничено DateTime до 1 мс или 16 мс (в зависимости от оборудования). Хотя метки времени будут уникальными, они не будут точно измерять тики. Подход @MusiGenesis даст более точные измерения. - person ToolmakerSteve; 24.09.2015

Все эти предложения выглядят слишком сложными! Если вы используете 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

person Jamezor    schedule 02.12.2014
comment
Конечно, все эти ответы были написаны еще до появления Windows 8 / Server 2012. И даже сегодня предполагать, что у клиентов будет 8/2012, для большей части кода - не лучший вариант. - person ToolmakerSteve; 24.09.2015

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

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

person Jason Kresowaty    schedule 12.09.2009
comment
Имеет ли смысл вызывать DateTime.Now при запуске приложения, а затем просто запустить StopWatch, а затем просто добавить истекшее время из StopWatch к начальному значению, возвращаемому из DateTime.Now, чтобы получить более точную метку времени? - person ; 13.09.2009
comment
Германн, разница в значениях будет точной, но база (DateTime.Now) по-прежнему будет иметь точность ~ 20 мс. То же самое и с абсолютными значениями, которые вы получите. - person Henk Holterman; 13.09.2009
comment
Да, но учитывая, что ваше системное время, вероятно, все равно не синхронизировано идеально, это смещение не будет иметь большого значения. По крайней мере, таким образом относительные временные метки более точны. - person ; 13.09.2009

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

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


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

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

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

t = t1 + (tt - tt0)

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

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

person Peter Mortensen    schedule 12.09.2009

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

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

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

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

person Nathaniel    schedule 29.12.2010
comment
Несмотря на то, что это дает торговый приказ, это не дает точности в миллисекундах для временных меток, как того требует OP. - person ToolmakerSteve; 24.09.2015

Точность 15 мс (на самом деле это может быть 15-25 мс) основана на разрешении таймера Windows 55 Гц / 65 Гц. Это также основной период TimeSlice. Затронуто Windows.Forms.Timer, Threading.Thread.Sleep, Threading.Timer и т. д.

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

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 нс. Этого должно хватить для вашей цели.

person Henk Holterman    schedule 12.09.2009
comment
-1, потому что квантование 15 мс основано на планировщике Windows. Разрешение таймера ~ 55 Гц взято из старого грубого механизма таймера, который запускает 18 событий в секунду. - person MusiGenesis; 13.09.2009
comment
Разрешение, которое я получаю в моей системе, составляет около 2,8 тика (2,79365114840015E-07), что довольно хорошо. Это в 50 000 раз точнее, особенно по сравнению с DateTime.NowUtc. - person ; 13.09.2009
comment
Musical Genius, 1/55 = 18 мс, так в чем разница? - person Henk Holterman; 13.09.2009
comment
@Henk: ой (Музыкальный гений). :) - person MusiGenesis; 13.09.2009
comment
@Henk: извините, я неправильно понял ваш исходный ответ, сказав, что 15 мс были основаны на разрешении таймера Windows 55 мс (которое оказалось разрешением старой школы 18 Гц таймер). Виноват. - person MusiGenesis; 13.09.2009

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

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

Вы знаете, что с этим решением вы не совсем точны, поэтому вы выполняете повторную синхронизацию. Но также то, что вы неявно хотите, - это то же самое, что и решение Яна Мерсера:

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

Следовательно, промежуток времени между двумя повторными синхронизациями (_maxIdle Назовем его SyncTime) должен зависеть от 4 факторов:

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

Очевидно, что первым ограничением для этой переменной будет:

коэффициент рассинхронизации ‹= коэффициент точности

Например: я не хочу, чтобы моя точность была хуже 0,5 с / час или 1 мс / день и т. Д. (На плохом английском: я не хочу ошибаться больше, чем 0,5 с / час = 12 с / день).

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

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

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

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

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

Возьмем пример:

Представьте себе соотношение рассинхронизации, о котором говорилось выше.

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

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

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

  • DateTime.UtcNow разрешение: 1 мс
  • коэффициент точности> = коэффициент рассинхронизации, давайте возьмем наиболее точный из возможных: коэффициент точности = коэффициент рассинхронизации
  • желаемый уровень точности: 1 мкс
  • Оценка коэффициента рассинхронизации вашего компьютера: 0,5 с / час (это тоже моя точность)

Если вы сбрасываете каждые 10 с, представьте, что у вас 9,999 с, за 1 мс до сброса. Здесь вы звоните в течение этого интервала. Время, в течение которого ваша функция будет строить график, опережает: 0,5 / 3600 * 9,999 с или 1,39 мс. Вы бы отобразили время 10.000390сек. Если после тика UtcNow вы совершите звонок в течение 390 микросекунд, у вас будет номер ниже предыдущего. Хуже, если этот коэффициент рассинхронизации является случайным в зависимости от загрузки процессора или других факторов.

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

Если я буду думать так же, то я опередил бы время на 0,139 микросекунды - хуже, чем мне нужна точность. Поэтому, если я вызываю функцию при 9,999 мс, то есть за 1 микросекунду до сброса, я построю график 9,999. И сразу после этого я построю 10.000. У меня будет хороший порядок.

Итак, здесь другое ограничение: коэффициент точности x уровень точности SyncTime ‹, скажем так, чтобы быть уверенным, потому что число может быть округлено в большую сторону, что коэффициент точности x уровень точности SyncTime‹ / 2 является хорошим.

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

Итак, краткий обзор:

  • Получите разрешение вашего таймера.
  • Вычислите оценку коэффициента рассинхронизации.
  • коэффициент точности> = оценка коэффициента несинхронизации, Наилучшая точность = коэффициент несинхронизации
  • Выберите свой уровень точности, учитывая следующее:
  • разрешение-таймера ‹= SyncTime‹ = PrecisionLevel / (2 * коэффициент точности)
  • Наилучшая точность, которую вы можете достичь, - это разрешение таймера * 2 * несинхронизация.

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

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

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

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

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

Наилучший уровень точности: в приведенном выше примере это 0,27 микросекунды.

Однако что произойдет, если я вызову его несколько раз между 9,999 мс и повторной синхронизацией.

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

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

person user3091460    schedule 09.03.2014
comment
Подход MusiGenesis лучше, если (1) смещение секундомера невелико (что так и есть), и (2) в запросах есть пропуски, достаточные для выполнения сброса. Единственная особенность, которой ему не хватает, - это автоинкремент от Яна (который можно легко добавить). Ни при каких обстоятельствах нельзя использовать Ian отдельно, если вы заботитесь о точности тиков: DateTime имеет точность только до 16 мс. - person ToolmakerSteve; 24.09.2015
comment
Если (2) представляет собой проблему из-за недостатка пропусков, то решение MG необходимо скорректировать для непрерывной коррекции: каждый вызов должен проверять, изменилось ли Now по сравнению с предыдущим вызовом. Если да, то сбросьте таймер, но используйте Макс Иэна (теперь lasttimestamp + 1), чтобы всегда увеличивать. - person ToolmakerSteve; 24.09.2015

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

person Piotr Czapla    schedule 12.09.2009