12

在我的电脑上,秒表返回的值太低了。例如,当我指定 200 毫秒时Thread.Sleep(1000)。该程序应该等待 1 秒。我也进行了测试ManualResetEvent.WaitOne(1000)并得到了相同的结果。框架 2.0 和 3.0 都给出了这种奇怪的行为。我正在运行带有 .NET Framework 3.5 SP1 的 Windows XP SP3。

这是我的测试结果(下面的代码):

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 类

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;
    }
}
4

7 回答 7

6

看看以下链接:

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

System.Diagnostics.Stopwatch 在 Elapsed... 属性中返回负数

秒表的准确性似乎存在问题,这可能导致它返回负的经过时间,并且时间变化很大。如果您查看 Connect 问题,那里的大多数人都在谈论在虚拟机上看到该问题,这就是我们看到负 Elapsed values 问题的地方。

查看QueryPerformanceCounter 文档,似乎暗示由于 BIOS 或硬件抽象层错误,此问题可能发生在多处理器系统上,但没有提供更多信息,也没有具体说明虚拟机。

至于这个问题的解决方案......我在很多谷歌搜索中都没有找到。您可以忽略小于零的值,这并不理想,但在某些情况下可行。但这并不能解决你的问题——你怎么知道哪些值是无效的?

希望这会有所帮助。

于 2011-10-27T16:52:23.750 回答
4

我知道这是一个老问题,但我想在遇到同样的问题后我会提供 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.TicksTimeSpan.FromTicks(sw.Elapsed.Ticks).ToString():返回“116013029”并在调用哪个准确 时转换为“00:00:11.6013029” 。

虽然我可能遗漏了一些东西,但我觉得 sw.​​ElaspedTicks 返回与 sw.Elapsed.Ticks 不同的值是没有意义的,所以如果有人想对此有所了解,请这样做,但从我的角度来看,我认为它是一个错误,如果不是,至少感觉非常不一致!

注意:调用sw.ElapsedTicks / Stopwatch.Frequency返回 11(即秒),但正如我所说,它会减少毫秒,这对我来说没有用。

于 2017-01-19T11:43:27.577 回答
1

我懂了:

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)

(无法运行最后一次测试)

在 Linqpad 中带有 .NET4 的四核 i7 机器上。

我只倾向于使用 Stopwatch.ElapsedMilliseconds 但我从未见过任何奇怪的地方。听起来您的机器或虚拟化平台确实有问题。

于 2011-02-25T09:57:49.967 回答
1

您可以使用该代码修复“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}");
于 2018-03-19T20:11:41.167 回答
0

如果秒表不起作用,您可以QueryPerformanceCounter在 Windows 上使用。

在http://www.windojitsu.com/code/hirestimer.cs.html上查看这个小课程

于 2011-02-25T09:43:32.540 回答
0

在某些情况下,您似乎正在使用 Tick 计数。请记住,例如,在现代 Windows 上默认情况下,操作系统将节省 CPU。这意味着滴答计数和经过的时间不是线性比例的。

我建议您尝试以Stopwatch.ElapsedMilliseconds最基本的形式使用:

var sw = new Stopwatch();
sw.Start();
Thread.Sleep(1000);
var elpased = sw.Elapsed;
于 2011-02-25T08:52:44.177 回答
-1

我相信你会在这里找到答案:http: //social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/eec73d6e-f597-4342-be48-5d2ce30202f0/

但有关更多详细信息和解释,另请参阅:http: //msdn.microsoft.com/en-us/magazine/cc163996.aspx http://stackoverflow.com/questions/394020/stopwatch-accuracy http://msdn.microsoft .com/en-us/library/system.diagnostics.stopwatch.aspx

于 2011-02-25T05:43:35.277 回答