简体   繁体   English

C#中的毫秒精确计时器

[英]Millisecond precise timer in C#

I am using a timer class (wrapper around System.Timers.Timer ), which I would like to use to display a 2 minute countdown in a WPF window, accurate to every 10 milliseconds. 我正在使用一个计时器类( System.Timers.Timer周围的包装器),我想用它在WPF窗口中显示2分钟的倒计时,精确到每10毫秒。 Basically, I'd like to dispay a string in the format mm\\\\:ss\\\\.ff that updates every 10 milliseconds. 基本上,我想以mm\\\\:ss\\\\.ff格式显示一个字符串,每10毫秒更新一次。 Here is the class I have: 这是我的课程:

using System;
using System.Timers;
using Proj.Utilities.Extensions;

namespace Proj.Framework
{
/// <summary>
/// Counts down in wall time, raising events at a specified updated period.
/// </summary>
public sealed class MillisecondTimer
{
    private readonly TimeSpan _initialTime;
    private readonly Timer _timerRep;
    private TimeSpan _timeRemaining;

    /// <summary>
    /// The time remaining in the countdown.
    /// </summary>
    public TimeSpan TimeRemaining
    {
        get { return _timeRemaining; }
        private set
        {
            _timeRemaining = value;
            if (_timeRemaining <= TimeSpan.Zero)
            {
                InvokeCountDownElapsed();
            }
        }
    }

    /// <summary>
    /// True if the timer is currently counting down; false otherwise.
    /// </summary>
    public bool IsCountingDown => _timerRep.Enabled;

    /// <summary>
    /// Raised every time the update period elapses.
    /// </summary>
    public event EventHandler TimeChanged;

    /// <summary>
    /// Raised when the entire countdown elapses.
    /// </summary>
    public event EventHandler CountDownElapsed;

    /// <summary>
    /// Creates a new CountDownTimer.
    /// </summary>
    /// <param name="countDownTime">
    /// The amount of time the timer should count down for.
    /// </param>
    /// <param name="updatePeriod">
    /// The period with which the CountDownTimer should raise events.
    /// </param>
    public MillisecondTimer(TimeSpan countDownTime, TimeSpan updatePeriod)
    {
        _initialTime = countDownTime;
        _timerRep = new Timer(10) { AutoReset = true };


        AttachEventHandlers();
    }

    private void AttachEventHandlers()
    {
        AttachedElapsedEventHandler();
        AttachCountDownElapsedEventHandler();
    }

    private void AttachedElapsedEventHandler()
    {
        _timerRep.Elapsed += OnElapsed;
    }

    private void AttachCountDownElapsedEventHandler()
    {
        CountDownElapsed += OnCountDownElapsed;
    }

    private void InvokeTimeChanged()
    {
        //Defined in Proj.Utilities.Extentions
        TimeChanged.InvokeIfInstantiated(this, new EventArgs());
    }

    private void InvokeCountDownElapsed()
    {
        CountDownElapsed.InvokeIfInstantiated(this, new EventArgs());
    }

    private void OnElapsed(object sender, ElapsedEventArgs e)
    {
        TimeRemaining -= TimeSpan.FromMilliseconds(10);
        InvokeTimeChanged();
    }

    private void OnCountDownElapsed(object sender, EventArgs e)
    {
        Stop();
    }

    /// <summary>
    /// Restarts the countdown.
    /// </summary>
    public void Restart()
    {
        TimeRemaining = _initialTime;
        _timerRep.Start();
        InvokeTimeChanged();
    }

    /// <summary>
    /// Stops the countdown.
    /// </summary>
    public void Stop()
    {
        _timerRep.Stop();
    }
}
}

It works, in that it does what I expect it to do, but it ends up being too slow. 它起作用,因为它做了我期望它做的事情,但它最终太慢了。 When I want it to countdown from 10 seconds, it takes about 15 seconds. 当我希望它从10秒倒计时时,大约需要15秒。 The ultimate goal is a class that can count down from 2 minutes with 10 millisecond resolution, accurately. 最终目标是能够准确地从10分钟,10毫秒分辨率开始倒数。 Why does this take longer than it should, and what could I do to make it work better? 为什么这需要的时间超过应有的时间,我能做些什么才能让它更好用呢?

Don't change your TimeRemaining each time - keep the starting time and compare to the current time to get TimeRemaining : 不要每次都更改TimeRemaining - 保持开始时间并与当前时间进行比较以获得TimeRemaining

TimeRemaining = (DateTime.Now - _initialTime);

That way any error you get from the Timer is not accumulated but evens out in the end. 这样,从Timer获得的任何错误都不会累积,而是最终会均衡。

I once wrote a wrapper around the Multimedia Timer API which can be accurate up 1 ms, see here . 我曾经写过一个围绕多媒体计时器API的包装器,它可以准确地达到1毫秒,见这里 Not tested for production use, so caveat emptor. 未经生产用途测试,因此请注意。

However, your approach seems incorrect to me. 但是,您的方法似乎对我不正确。 Ask yourself if you really need accurate 10 ms timing to display a countdown for a 2 minute interval. 问问自己是否真的需要准确的10 ms时间来显示2分钟间隔的倒计时。 You seem to be putting two responsibilities on the timer. 你似乎在计时器上放了两个责任。 Keeping accurate time and raising events periodically. 保持准确的时间并定期举办活动。 Timers are good only for latter of these two. 定时器仅适用于这两者中的后者。 Use Stopwatch to keep time accurately. 使用Stopwatch准确地保持时间。 Use a timer to take the elapsed time from the stopwatch and update a display periodically. 使用计时器从秒表获取经过的时间并定期更新显示。 That way time is kept accurately regardless of the accuracy of the timer. 这样,无论计时器的准确性如何,时间都能保持准确。 Users aren't going to notice slight deviations in how long the timer takes to elapse then and aren't going to care if it updates every 10 or 20ms. 用户不会注意到计时器经过多长时间的微小偏差,如果每10或20ms更新一次,则不会关心。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM