C#中的毫秒精确计时器

时间:2015-10-26 17:31:48

标签: c# wpf timer

我正在使用一个计时器类(System.Timers.Timer周围的包装器),我想用它在WPF窗口中显示2分钟的倒计时,精确到每10毫秒。基本上,我想以mm\\:ss\\.ff格式发送一个每10毫秒更新一次的字符串。这是我的课程:

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();
    }
}
}

它起作用,因为它做了我期望它做的事情,但它最终太慢了。当我希望它从10秒倒计时时,大约需要15秒。最终目标是能够准确地从10分钟,10毫秒分辨率开始倒数。为什么这需要的时间超过应有的时间,我能做些什么才能让它更好用?

2 个答案:

答案 0 :(得分:3)

每次都不要更改TimeRemaining - 保持开始时间并与当前时间进行比较以获得TimeRemaining

TimeRemaining = (DateTime.Now - _initialTime);

这样一来,你从Timer得到的任何错误都没有累积,但最终还是平衡了。

答案 1 :(得分:0)

我曾经写过一个围绕多媒体计时器API的包装器,它可以准确地延长1毫秒,参见here。未经生产用途测试,因此请注意。

然而,你的方法对我来说似乎不对。问问自己是否真的需要准确的10 ms时间来显示2分钟间隔的倒计时。你似乎在计时器上放了两个责任。保持准确的时间并定期举办活动。定时器仅适用于这两者中的后者。使用Stopwatch可以准确地保持时间。使用计时器从秒表获取经过的时间并定期更新显示。这样,无论计时器的准确性如何,时间都能保持准确。用户不会注意到计时器经过多长时间后会出现轻微的偏差,如果每10或20ms更新一次,则不会关注。