【发布时间】:2015-10-26 17:31:48
【问题描述】:
我正在使用一个计时器类(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 毫秒的分辨率准确地从 2 分钟倒计时的课程。为什么这需要比预期更长的时间,我可以做些什么来让它更好地工作?
【问题讨论】:
-
"精确到每 10 毫秒" 不是 windows,我认为最小分辨率约为 15 毫秒,调度/抢占可以推得更高。您可能会看到结果(这就是为什么 10 秒看起来需要 15 秒的原因)。如果您想要一个准确的 10 秒计时器(具有可变的更新间隔),那么您需要两个计时器,一个用于整个周期,一个用于更新,或者与日期/时间进行比较,但也存在分辨率问题。