.NET System.Diagnostics.Stopwatch проблема (возвращает значения слишком низко) - PullRequest
12 голосов
/ 25 февраля 2011

На моем компьютере секундомер возвращает значения слишком низко.Например, 200 мс, когда я указал Thread.Sleep(1000).Программа должна ждать 1 секунду.Я также проверил с ManualResetEvent.WaitOne(1000) и получил те же результаты.И фреймворк 2.0, и 3.0 дают такое странное поведение.Я использую Windows XP SP3 с .NET Framework 3.5 SP1.

Вот результат моих тестов (код ниже):

1000 ms for DateTime.Now.Ticks
0201 ms for Stopwatch.ElapsedTicks
0142 ms for Stopwatch.ElapsedMilliseconds
0139 ms for Stopwatch.ElapsedTicks after Reset
0264 ms for Stopwatch.ElapsedTicks setting ThreadAffinity
0151 ms for Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)
0371 ms for Stopwatch.ElapsedTicks with Syncronized object
Done!

// Файл Program.cs

class Program
{
    static void Main(string[] args)
    {
        StopWatchTest.Go();
        Console.WriteLine("Done!");
        Console.ReadLine();
    }
}

// StopWatchTest.cs class

internal static class StopWatchTest
{
    public const int SleepTime = 1000;

    public static void Go()
    {
        #region Test #0 with DateTime.Now.Ticks
        long startTick = DateTime.Now.Ticks;
        Thread.Sleep(SleepTime);
        long stopTick = DateTime.Now.Ticks;
        long elapsedDt = (stopTick - startTick) * 100;
        Display((int)(elapsedDt / 1000 / 1000), "DateTime.Now.Ticks");
        #endregion Test #0 with DateTime.Now.Ticks

        Stopwatch watch = Stopwatch.StartNew();
        long frequency = Stopwatch.Frequency;
        double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / frequency;

        #region Test #1 with Stopwatch.ElapsedTicks
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        double elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks");
        #endregion Test #1 with Stopwatch.ElapsedTicks

        #region Test #2 with Stopwatch.ElapsedMilliseconds
        startTick = watch.ElapsedMilliseconds;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedMilliseconds;
        Display((int)(stopTick - startTick), "Stopwatch.ElapsedMilliseconds");
        #endregion Test #2 with Stopwatch.ElapsedMilliseconds

        #region Test #3 with Stopwatch.ElapsedTicks after Reset
        watch.Stop();
        watch.Reset();
        watch.Start();
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks after Reset");
        #endregion Test #3 with Stopwatch.ElapsedTicks after Reset

        #region Test #4 with Stopwatch.ElapsedTicks and ThreadAffinity
        Thread.BeginThreadAffinity();
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks setting ThreadAffinity");
        Thread.EndThreadAffinity();
        #endregion Test #4 with Stopwatch.ElapsedTicks and ThreadAffinity

        #region Test #5 with Stopwatch.ElapsedTicks and ProcessorAffinity (and more)
        const int affinity = 0x0001;
        Process proc = Process.GetCurrentProcess();
        proc.ProcessorAffinity = new IntPtr(affinity);
        proc.PriorityClass = ProcessPriorityClass.High;
        ProcessThreadCollection ptc = proc.Threads;
        foreach (ProcessThread pt in ptc)
        {
            pt.IdealProcessor = 0;
            pt.ProcessorAffinity = new IntPtr(affinity);
        }
        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)");
        #endregion Test #5 with ProcessorAffinity and more

        #region Test #6 with Syncronized object
        elapsedSw = new SyncTimer().Go();
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks with Syncronized object");
        #endregion Test #6 with Syncronized object
    }

    private static void Display(int milliseconds, string testName)
    {
        Console.WriteLine("{0:0000} ms for {1}", milliseconds, testName);
    }
}

[Synchronization]
internal class SyncTimer : ContextBoundObject
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public double Go()
    {
        Stopwatch.StartNew();
        long frequency = Stopwatch.Frequency;
        double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / frequency;

        long startTick = Stopwatch.GetTimestamp();
        Thread.Sleep(StopWatchTest.SleepTime);
        long stopTick = Stopwatch.GetTimestamp();
        return (stopTick - startTick) * nanosecPerTick;
    }
}

Ответы [ 7 ]

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

Просмотрите следующие ссылки:

https://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time

System.Diagnostics.Stopwatch возвращает отрицательные числа в Elapsed ... properties

Кажется, есть проблема с точностью секундомера, которая может привести к тому, что он возвращает отрицательные прошедшие времена, а также дико изменяющиеся времена.Если вы посмотрите на проблему с подключением, то большинство людей там говорят о том, что видели проблему на виртуальной машине, и именно здесь мы наблюдаем проблему отрицательных значений Elapsed., кажется, это означает, что эта проблема может возникать в многопроцессорных системах из-за ошибок BIOS или уровня абстрагирования оборудования, но не дает никакой дополнительной информации и не относится к виртуализированным машинам.... Я не нашел ни одного в поиске.Вы можете игнорировать значения меньше нуля, что не идеально, но в некоторых случаях работает.Но это не решает вашу проблему - как вы узнаете, какие значения являются недействительными?

Надеюсь, это поможет.

2 голосов
/ 19 января 2017

Я знаю, что это старый вопрос, но я думал, что предоставлю свои 2 цента после борьбы с той же самой проблемой:

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

В любом случае, после большого количества перемоток назад и бездействия, я заметил, что sw.Elapsed имеет свойство Ticksи этот предоставил мне точное количество тиков, а когда он был преобразован обратно, он дал мне точное время.

Код мудрый, вот что я закончил:

Stopwatch sw = new Stopwatch();
sw.Start();

... DO WORK

sw.Stop();

long elapsedTicks = sw.Elapsed.Ticks;
Debug.WriteLine(TimeSpan.FromTicks(elapsedTicks).ToString());

Когдавыполнение теста, вызов:

  • sw.Elapsed.ToString(): «00: 00: 11.6013029»

  • sw.ElapsedTicks: возвращает «40692243» ипреобразуется в «00: 00: 04.0692243» при вызове TimeSpan.FromTicks(sw.ElapsedTicks).ToString(), что является неточным.

  • sw.Elapsed.Ticks: возвращает «116013029» и преобразуется в «00: 00: 11.6013029» при вызовеTimeSpan.FromTicks(sw.Elapsed.Ticks).ToString() который точен.

Хотя я могу что-то упустить, я чувствую, что не имеет смысла, что sw.ElaspedTicks возвращает другое значение, чем sw.Elapsed.Ticks, поэтому, если кто-то хочет пролить свет на это, пожалуйста, но с моей точки зрениязрения, я вижу это как ошибку, и если нет, то, по крайней мере, она кажется очень непоследовательной!.

ПРИМЕЧАНИЕ : вызов sw.ElapsedTicks / Stopwatch.Frequency возвращает 11 (т. е. секунд), но, как я сказал,он отбрасывает миллисекунды, которые мне не нужны.

1 голос
/ 25 февраля 2011

Я получил это:

1000 ms for DateTime.Now.Ticks
0999 ms for Stopwatch.ElapsedTicks
1000 ms for Stopwatch.ElapsedMilliseconds
0999 ms for Stopwatch.ElapsedTicks after Reset
0999 ms for Stopwatch.ElapsedTicks setting ThreadAffinity
0999 ms for Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)

(не удалось запустить последний тест)

На четырехъядерном компьютере i7 с .NET4 в Linqpad.

Я всегда склонен использовать секундомер. Прошло миллисекунд, но я никогда не видел в этом ничего странного. Звучит так, будто на вашей машине или платформе виртуализации что-то сломано.

0 голосов
/ 19 марта 2018

Вы можете использовать этот код для решения проблемы метода «Stopwatch.Elapsed»:

using System;
using System.Diagnostics;

namespace HQ.Util.General
{
    public static class StopWatchExtension
    {
        public static TimeSpan ToTimeSpan(this Stopwatch stopWatch)
        {
            return TimeSpan.FromTicks(stopWatch.ElapsedTicks);
        }
    }
}

Использование:

using HQ.Util.General;

Debug.Print($"Elapsed millisec: { stopWatch.ToTimeSpan().TotalMilliseconds}");
0 голосов
/ 25 февраля 2011

Если секундомер не работает, вы можете использовать QueryPerformanceCounter в Windows.

См. Этот маленький класс на http://www.windojitsu.com/code/hirestimer.cs.html

0 голосов
/ 25 февраля 2011

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

Я предлагаю вам попробовать Stopwatch.ElapsedMilliseconds в наиболее простой форме:1008 *

var sw = new Stopwatch();
sw.Start();
Thread.Sleep(1000);
var elpased = sw.Elapsed;
0 голосов
/ 25 февраля 2011

Я полагаю, вы найдете ответ здесь: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/eec73d6e-f597-4342-be48-5d2ce30202f0/

, но для более подробной информации и объяснения см. Также: http://msdn.microsoft.com/en-us/magazine/cc163996.aspx /342800/naskolko-tochen-system-diagnostics-stopwatch http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

...