.net c#带TimeSpan的计时器和自制计时器比秒表慢

Timer with TimeSpan and self-made timer are slower than Stopwatch
2020-11-21
  •  译文(汉语)
  •  原文(英语)

我正在尝试开发简单的计时器,该计时器可以保存其上一个值,并在新应用启动时从其继续.

Stopwatchclass不可序列化,甚至无法初始化以从特定时间启动.但是效果很好.基准显示秒表的1分钟实际上是1分钟.

我尝试TimeSpan通过以下方式使用:

private TimeSpan timerNew = new TimeSpan();
private DispatcherTimer dispatcherTimer = new DispatcherTimer();

public MainWindow()
{
    InitializeComponent();

    dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
    dispatcherTimer.Tick += Timer_Tick;
}

private void Timer_Tick(object sender, EventArgs e)
{
    timerNew += new TimeSpan(0, 0, 0, 1);
    TbTimer.Text = String.Format("{0:00}:{1:00}:{2:00}",
        timerNew.Hours, timerNew.Minutes, timerNew.Seconds);
}

private void ButtonStart_OnClick(object sender, RoutedEventArgs e)
{
    dispatcherTimer.Start();
}

private void ButtonStop_OnClick(object sender, RoutedEventArgs e)
{
        dispatcherTimer.Stop();
}

private void ButtonReset_OnClick(object sender, RoutedEventArgs e)
{
    timerNew = new TimeSpan();
    TbTimer.Text = "00:00:00";
}

当我对照真正的秒表对其进行检查时,我发现此计时器实现每分钟损失2秒.

我还尝试了自己的Timer实现,该实现是带有ulong字段的简单类,在每个dispatcherTimer刻度上递增.UI在将秒转换为小时,分钟等之后显示结果.但与真正的秒表相比,它每分钟也会损失2秒.

为什么这2秒会丢失?Stopwatch在可定制计时器中使用的替代方法是什么?

速聊1:
这是完全正常的,计时器无法使事件处理程序立即运行.仅使用挂钟测量经过的时间.DateTime.UtcNow.
解决过程1

Windows线程调度程序不是"实时"调度程序,因为Windows不是"实时操作系统".换句话说,所有时序和调度都是在"尽力而为"的基础上完成的,而没有任何精确度的保证.另外,这总是会浪费时间,因为您可以确保的是,安排不会提早进行.因此,当存在不精确性时,总是朝着"后期"方向发展.

Stopwatch类的工作,因为它使用的CPU支持的性能计数器,它不依赖于操作系统的调度.硬件本身会跟踪经过的时间并提供您所需的信息.

我建议不要使用DateTime.UtcNow来测量经过的时间,这有两个原因:首先,时钟的DateTime使用是可调整的,因此即使使用UTC时间(至少可以补偿由于夏令时引起的自动调整)也不能保证是准确的.其次,您的特定方案似乎涉及到一个问题,您要序列化当前状态并还原它,DateTime.UtcNow无论如何都无法解决.

相反,您应该制作自己的可序列化的秒表类,该类以Stopwatch自身为基础,但存储了已添加到Stopwatch的已用值的基本已用值.

例如:

class SerializableStopwatch
{
    public TimeSpan BaseElapsed { get; set; }
    public TimeSpan Elapsed { get { return _stopwatch.Elapsed + BaseElapsed; } }

    private Stopwatch _stopwatch = new Stopwatch();

    // add whatever other members you want/need from the Stopwatch class,
    // simply delegating the operation to the _stopwatch member. For example:

    public void Start() { _stopwatch.Start(); }
    public void Stop() { _stopwatch.Stop(); }
    // etc.
}

您将如何序列化以上内容完全取决于您.在最简单的情况下,您可以将Elapsed属性设置为字符串格式以保存值,然后在要还原对象时解析该值,创建上述类的新实例,然后将值分配给BaseElapsed属性.


有关该主题的其他讨论,您可能会发现Eric Lippert的博客文章DateTime的Precision和准确性非常有用且有趣.

速聊1:
谢谢,这很有趣.关于如何序列化秒表的想法确实是很酷的解决方案.
速聊2:
你已经倒退了.DateTime.UtcNow准确的,由时间服务保持这种方式.是秒表不准确,根本没有校准.精度仅与电子零件公差一样好,对于如今的廉价硬件而言,无可置疑.
速聊3:
:您阅读过Eric的文章吗?还是其他关于特定主题的?如果您想知道现在是几点,请​​使用DateTime.UtcNow.但是对于测量经过的时间,它是不可靠的.和Stopwatch .请注意,时钟用来跟踪时间的天在PC上(正如你说的往往是不准确的,因此需要通过NTP定期更新......的确,这需要定期更新是另一个理由来使用系统时钟来测量经过时间)与用于的时钟不同Stopwatch.
速聊4:
您无需电子专业学位即可自行解决.如果秒表的频率源是如此精确,那么为什么他们不也使用它来保持壁钟精确呢?
速聊5:
:对您问题的简短回答是,这样做是不切实际的.网络上有很多文章可以解释PC日时钟使用的所谓实时时钟与使用基于CPU的性能计数器Stopwatch使用的高精度时钟之间的区别,以及它驱动了OS的其他各种高精度定时需求(例如A / V播放),以及为什么两者在OS中的用法不同.不,您不需要EE学位,但您也应该警惕遵循自己的直觉,因为它并不像您想象的那么简单.

I'm trying to develop simple timer which can save its last value and continue from it at the new app start.

Stopwatch class is not serializable and even cannot be initialized in order to be started from specific time. But it works great. Benchmark showed that stopwatch's 1 minute is really 1 minute.

I tried to use TimeSpan in the following way:

private TimeSpan timerNew = new TimeSpan();
private DispatcherTimer dispatcherTimer = new DispatcherTimer();

public MainWindow()
{
    InitializeComponent();

    dispatcherTimer.Interval = TimeSpan.FromSeconds(1);
    dispatcherTimer.Tick += Timer_Tick;
}

private void Timer_Tick(object sender, EventArgs e)
{
    timerNew += new TimeSpan(0, 0, 0, 1);
    TbTimer.Text = String.Format("{0:00}:{1:00}:{2:00}",
        timerNew.Hours, timerNew.Minutes, timerNew.Seconds);
}

private void ButtonStart_OnClick(object sender, RoutedEventArgs e)
{
    dispatcherTimer.Start();
}

private void ButtonStop_OnClick(object sender, RoutedEventArgs e)
{
        dispatcherTimer.Stop();
}

private void ButtonReset_OnClick(object sender, RoutedEventArgs e)
{
    timerNew = new TimeSpan();
    TbTimer.Text = "00:00:00";
}

When I checked it against real stopwatch, I found out that this timer implementation lost 2 seconds per minute.

I also tried my own Timer implementation which is simple class with ulong field, which is incremented on each dispatcherTimer tick. And UI shows results after transformation of seconds to hours, minutes and so on. But it also loses 2 seconds per minute comparing to real stopwatch.

Why these 2 seconds are lost? What is an alternative to Stopwatch for usage in a customizable timer?

Talk1:
That is entirely normal, timers can't get their event handlers running instantly. Only use the wall clock to measure elapsed time. DateTime.UtcNow.
Solutions1

The Windows thread scheduler is not a "real-time" scheduler, as Windows is not a "real-time OS". In other words, all timing and scheduling is done on a "best effort" basis, without any guarantee of exact precision. In addition, this always results in lost time, because the one guarantee you do have is that scheduling will not happen early. So when there's an imprecision, it's always in the direction of "late".

The Stopwatch class works because it uses CPU-supported performance counters, which doesn't rely on the OS scheduler. The hardware itself tracks the elapsed time and provides the information you need.

I recommend against the use of DateTime.UtcNow for measuring elapsed time, for two reasons: first, the clock DateTime uses is adjustable, and so even using UTC time (which at least would compensate for automatic adjustments due to Daylight Saving Time) is not guaranteed to be accurate. Second, your specific scenario seems to involve an issue where you want to serialize the current state and restore it, which DateTime.UtcNow doesn't address anyway.

Instead, you should make your own serializable stopwatch class, which uses Stopwatch itself as the basis, but which stores a base elapsed value that you add to the Stopwatch's elapsed value.

For example:

class SerializableStopwatch
{
    public TimeSpan BaseElapsed { get; set; }
    public TimeSpan Elapsed { get { return _stopwatch.Elapsed + BaseElapsed; } }

    private Stopwatch _stopwatch = new Stopwatch();

    // add whatever other members you want/need from the Stopwatch class,
    // simply delegating the operation to the _stopwatch member. For example:

    public void Start() { _stopwatch.Start(); }
    public void Stop() { _stopwatch.Stop(); }
    // etc.
}

How exactly you would serialize the above is up to you. In the simplest scenario, you can just format the Elapsed property as a string to save the value, and then when you want to restore the object, parse that value, create a new instance of the above class, and then assign the value to the BaseElapsed property.


For additional discussion on the topic, you might find Eric Lippert's blog article Precision and accuracy of DateTime useful and interesting.

Talk1:
Thanks, it is very interesting. And the idea on how to serialize Stopwatch is really cool solution.
Talk2:
You've got that pretty backwards. DateTime.UtcNow is accurate, kept that way by a time service. It is Stopwatch that is not accurate, it doesn't get calibrated at all. Accuracy is only as good as electronic part tolerances, nothing to put any faith in with today's dirt-cheap hardware.
Talk3:
: did you read Eric's article? Or others on the specific topic? If you want to know what time of day it is, by all means, use DateTime.UtcNow. But for measuring elapsed time, it is not reliable. And Stopwatch is. Please note that the clock used to track time-of-day on a PC (which as you say is often not accurate, hence the need to periodically update via NTP...indeed, this need to update periodically is yet another reason not to use the system clock for measuring elapsed time) is not the same clock used for Stopwatch.
Talk4:
You can reason this out for yourself without a degree in electronics. If the frequency source for Stopwatch is so fantastically accurate, why did they not also use it to keep the wall clock accurate?
Talk5:
: the short answer to your question is that to do so would be impractical. There are lots of articles on the web that can explain the difference between the so-called real-time clock used for the PC time-of-day clock and the high-precision clock used by the CPU-based performance counters that Stopwatch uses, and which drives the various other high-precision timing needs of the OS (e.g. A/V playback), and why the two are used differently in the OS. And no, you don't need an EE degree, but you should also be wary of following your intuition, as it's not as simple as you think.
转载于:https://stackoverflow.com/questions/28398358/timer-with-timespan-and-self-made-timer-are-slower-than-stopwatch

本人是.net程序员,因为英语不行,使用工具翻译,希望对有需要的人有所帮助
如果本文质量不好,还请谅解,毕竟这些操作还是比较费时的,英语较好的可以看原文

留言回复
我们只提供高质量资源,素材,源码,坚持 下了就能用 原则,让客户花了钱觉得值
上班时间 : 周一至周五9:00-17:30 期待您的加入