C # TimeSpan.FromTicks () неточно? - PullRequest
       21

C # TimeSpan.FromTicks () неточно?

6 голосов
/ 18 октября 2011

Я уже некоторое время использую C # для создания маленькой игры, и во время тестирования этой игры на другом ПК я столкнулся с некоторыми странными проблемами прошедшего времени.

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

Я обнаружил, что проблема заключается в создании TimeSpan с использованиемFromTicks() метод.Я провел небольшой тест, используя следующий код:

class Program
{
    static void Main(string[] args)
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
        sw.Stop();
        TimeSpan t = TimeSpan.FromTicks(sw.ElapsedTicks);
        Console.WriteLine(t.ToString());
        Console.WriteLine(sw.Elapsed.ToString());
        Console.ReadKey();
    }
}

На своем основном ПК я запустил эту программу и получил следующее:

    00:00:00.3528353
    00:00:00.9856987

Что-то, чего я совсем не ожидал,Я думал, что второй результат был довольно неточным, но первый был хорошо.

Затем я запустил ту же программу на другом ПК и получил это:

    00:03:20.6866734
    00:00:00.998287

Я был совершенно изумлен.

Мой вопрос здесь не в том, как я могу решить эту проблему, я уже решил использовать второй метод, потому что он достаточно точен ... скорее, я прошу просветления.

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

Я проверил msdn на случай, если я использовал неправильный метод, но примеры показывают, что мои результаты должны быть невозможны ...

Примечание:
Я думаю, что батарея CMOS умирает / разрядилась, это фактор?

Ответы [ 3 ]

6 голосов
/ 18 октября 2011

Резюме: Частота секундомера может отличаться на разных аппаратных средствах, что означает, что тики (чей интервал основан на частоте) имеют другой размер (и разный размер по сравнению с тиком в объекты timepan и datetime).

Короче говоря, вместо этого используйте свойство Elapsed напрямую:

    TimeSpan t = sw.Elapsed;

... или используйте свойство Ticks Elapsed, если вам нужно выполнить вычисления:

    TimeSpan t = TimeSpan.FromTicks(2*sw.Elapsed.Ticks);

Длинная версия со ссылками:

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx - это страница MSDN для прошедших тиков. Следует отметить:

Это свойство представляет количество прошедших тиков в базовом таймерный механизм. Тик - это самая маленькая единица времени, Таймер секундомера можно измерить. Используйте поле частоты для преобразования Значение ElapsedTicks за несколько секунд.

А со страницы в поле Частота:

Частота таймера указывает точность и разрешение таймера. За Например, частота таймера 2 миллиона тиков в секунду равна разрешение по таймеру 500 наносекунд на такт. Другими словами, потому что одна секунда равна 1 миллиарду наносекунд, частота таймера 2 миллион тиков в секунду эквивалентен 2 миллионам тиков в 1 миллиард наносекунд, которые могут быть дополнительно упрощены до 1 тика на 500 наносекунд.

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

Поскольку частота секундомера зависит от установленного оборудования и В операционной системе значение частоты остается постоянным, в то время как система работает.

Таким образом, по существу, частота секундомера может отличаться на разных аппаратных средствах, что означает, что отметки имеют разный размер (и разный размер по сравнению с отметкой в ​​объектах timepan и datetime).

Интересно, что вы уже используете свойство Elapsed StopWatch, которое дает вам промежуток времени. sw.Elapsed - это TimeSpan, который, вероятно, то, что вам нужно, когда вы пытаетесь получить объект TimeSpan. Если вы хотите использовать тики, вы можете вместо этого использовать свойство Ticks этого TimeSpan.

В качестве альтернативы вы можете использовать ElapsedMilliseconds, который возвращает long.

6 голосов
/ 18 октября 2011

Тики Timespan и Tick секундомера различны. Для согласования используйте вместо этого свойство Elapsed секундомера, которое корректно преобразуется в интервал времени, основанный на тиках машины.

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); 
    sw.Start();         
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));         
    sw.Stop();         

    // don't load a TimeSpan with ElapsedTicks, these have diff frequency,
    // call Elapsed instead to get TimeSpan.
    TimeSpan t = sw.Elapsed;         
    Console.WriteLine(t.ToString());         
    Console.ReadKey(); 

Или, конечно, вы можете делить на частоту и т. Д. Сами, но это намного больше работы. Главное, что вы не должны считать Тики TimeSpan такими же, как Тики Секундомера.

Похожие темы: Что такое таймеры, единицы измерения, используемые секундомером. ElapsedTicks

0 голосов
/ 19 ноября 2016

Только для вашей информации ... Я проверил это, и вот как это сделать, если вы абсолютно хотите использовать «FromTicks».Но на вашем месте я бы последовал совету Джеймса Майкла Хейра.

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
    sw.Stop();

    // don't load a TimeSpan with ElapsedTicks, these have diff frequency,
    // call Elapsed instead to get TimeSpan.
    TimeSpan t1 = sw.Elapsed;
    Debug.Print("Millisecs: " + t1.TotalMilliseconds);


    // TimeSpan.Elapsed Code
    //if (!SafeNativeMethods.QueryPerformanceFrequency(out Stopwatch.Frequency))
    //{
    //  Stopwatch.IsHighResolution = false;
    //  Stopwatch.Frequency = 10000000L;
    //  Stopwatch.tickFrequency = 1.0;
    //}
    //else
    //{
    //  Stopwatch.IsHighResolution = true;
    //  Stopwatch.tickFrequency = 10000000.0;
    //  Stopwatch.tickFrequency /= (double)Stopwatch.Frequency;
    //}

    //public TimeSpan Elapsed
    //{
    //  [__DynamicallyInvokable]
    //  get
    //  {
    //      return new TimeSpan(this.GetElapsedDateTimeTicks());
    //  }
    //}

    //private long GetElapsedDateTimeTicks()
    //{
    //  long rawElapsedTicks = this.GetRawElapsedTicks();
    //  if (Stopwatch.IsHighResolution)
    //      return (long)((double)rawElapsedTicks * Stopwatch.tickFrequency);
    //  return rawElapsedTicks;
    //}

    TimeSpan t2;
    if (Stopwatch.IsHighResolution)
    {
        t2 = TimeSpan.FromTicks((long)((double)sw.ElapsedTicks * ((double)10000000.0 / (double)Stopwatch.Frequency)));
    }
    else
    {
        t2 = TimeSpan.FromTicks(sw.ElapsedTicks);
    }
    Debug.Assert(t1.TotalMilliseconds == t2.TotalMilliseconds); // true, 

    return;
...