【问题标题】:How to make an accurate decimal Timer?如何制作准确的十进制计时器?
【发布时间】:2012-07-20 23:55:00
【问题描述】:

我对此感到非常沮丧..

我有一个名为 timer1 的计时器和一个名为 TimeElapsedTextBox 的文本框和一个名为 double 的变量 TimeTakenToFinish 计时器每 1 秒(1000 毫秒)计时一次 在文本框中,我希望它以这种格式显示时间:

Seconds.PartsOfSecond

这里是 Tick 事件:

private void timer1_Tick(object sender, EventArgs e)
{
  TimeTakenToFinish += (double)timer1.Interval / 10000;
  TimeElapsedTextBox.Text = TimeTakenToFinish;
}

它实际上是以我想要的方式在文本框中显示它, 但它没有正确计算.. 我的意思是,它的计数不到一秒..

你能告诉我如何解决这个问题吗..

【问题讨论】:

  • 不要只在每个刻度上添加请求的间隔。相反,您应该查看实际经过了多少时间,并将该数字添加到TimeTakenToFinish
  • 如果您唯一的问题是需要更高的精度,请考虑System.Diagnostics.Stopwatch 类。它有一个静态属性IsHighResolution,其true 值表明您的处理器支持高分辨率计时器。有关详细信息,请参阅msdn.microsoft.com/en-us/library/…
  • 您需要在秒表上使用 .Elapsed(以及任何 .ElapsedXXX 属性)。您当前使用的方法每个滴答声都会产生一些舍入误差,因为计时器不保证会以您准确的速率触发告诉它。这将导致“你的时间”每滴答一下就偏离现实。使用“delta”方法(记录starttime,然后每个刻度显示starttime - currenttime)保持准确。甚至更好:使用 Stopwatch 类,它旨在做你想做的事。
  • @Kivin 我不认为 Vexe 需要高分辨率。对于大多数在表格上测量和显示的东西来说,“常规”秒表就可以了。我不认为他在这里计算 CPU 周期;)他的问题是他使用的方法(假设计时器每 1000 毫秒触发一次 exactly* 而它没有)。使用这种方法,即使是高分辨率计时器最终也会消失。 Windows(和大多数操作系统)不是 RT-OS(甚至那些最终也会漂移)
  • @EdS。一切都很好,但您并不需要如此高的准确度。因此,即使IsHighResolution 出于某种原因返回 false,它仍然足以准确地测量 Vexe 尝试执行的“CPS/WPM”。假设 Vexe 现在有了这些答案,至少能够测量一分钟而不会使每个“滴答”漂移几毫秒,您所需要做的就是计算一分钟的击键次数,这将导致 CPS/WPM 分数 +/- .01 .谁在乎测量是否“意外” 00:01:00.015 ?这不影响分数。如果您测量的是 F1 圈速,那么您需要更高的准确度。

标签: c# winforms timer


【解决方案1】:

您的问题是对您的操作系统工作方式的误解。当然,您可以将时间间隔设置为1000ms,但您不能指望它实际上每秒滴答一次。您在 Windows 上运行代码,而不是硬(或软)实时操作系统。

顺便说一句,您还应该知道您的计时器的分辨率是有限的,并且到今天为止,仅限于您的系统计时器的精度,大概是 15 毫秒左右。

您不能期望您的代码在那种环境中确定性地执行该操作。在任何时候,操作系统都可以抢先将您从 CPU 中踢出并开始处理另一项任务。

虽然我会问,但您根本无法获得所需的准确性;它实际上是必需的吗?可能不是,但你还没有告诉我们你在这里实际想要完成什么,所以谁知道呢?

另外,这是错误的:

TimeTakenToFinish += (double)timer1.Interval / 10000;

Interval 是一个属性,用于告诉计时器它应该多久触发一次Tick 事件。您实际上并没有测量任何东西,您不妨每次都将1000.0 / 10000 添加到您的计数器中。

如果您需要更高的精度,请使用StopWatch 类,该类使用您的 CPU 的高性能计时器(如果有)。您仍然可以使用计时器根据秒表的当前经过值定期更新 UI,即,

void timer1_Tick(...)
{
    var totalSeconds = _someStopwatch.ElapsedMilliseconds / 1000.0;
    TimeElapsedTextBox.Text = totalSeconds.ToString();
}

【讨论】:

  • 实际上,当我像这样除以 1000 时:TimeTakenToFinish += (double)timer1.Interval / 1000;我在文本框中得到了准确的秒数。并回答你问,是的,我需要它是准确的.. 为什么?因为我需要那个时间来计算打字程序的 CPS 和 WPM,时间越准确,两者就越准确。
  • @VeXe:不。你认为你得到了准确的结果。实际上,每次引发计时器的 Tick 事件时,您只需将 1000.0 / 10000 添加到计数器。 Interval 属性不是记录滴答之间经过的时间,它是您在启动计时器之前设置的静态值。你没有测量任何东西。使用秒表进行准确计时,这就是它的用途。
  • @VeXe "是的,我需要它准确.. 为什么?因为我需要那个时间来计算打字程序的 CPS 和 WPM,时间越准确,两者更准确”。很好,但是您需要在这里正确看待“准确性”。为此目的,1 甚至 10 毫秒或 100 毫秒的分辨率就足够了; 除非您的测量值正在漂移(您的测量值会漂移)。此外,测量周期越长,1 位或 2 位小数 CPS/WPM 结果所需的准确度就越低。想想吧。
【解决方案2】:

不使用计时器,而是使用 DateTime.Now 记录开始时间,然后从开始时间中减去当前时间 (DateTime.Now)。这将为您提供更准确的计时器,因为它使用不受 CPU 性能影响的系统时钟。

或者,您可以使用 System.Diagnostics.Stopwatch 为您执行此操作。

您仍然可以使用间隔小于一秒的普通计时器来刷新显示时间的标签。

【讨论】:

  • DateTime.Now 的分辨率约为 15 毫秒,与计时器大致相同。如果您的代码对 CPU 性能敏感,我看不出调用 DateTime.Now 会比使用计时器更好。您的代码没有运行,您使用哪种方法有什么关系?
  • @EdS。当您记录starttime 并在表单中显示每个“更新”(例如勾选)时,您将显示 delta(例如starttime - currenttime),您将永远不会超过约 15 毫秒谈论。
  • 如果您的代码没有运行以从记录的 DateTime 中减去当前时间。现在,您将无法获得准确性。您建议工作的唯一方法是坐在一个循环中,尽可能多地使用 CPU,将当前时间与记录时间进行比较。坏主意。
  • @RobIII:是的,这是真的。我说的是 OP 的预期目标,即每 X 毫秒运行一些代码。
  • @Eds 啊,不知道。我假设 Stopwatch 类使用更准确的方法。我认为关键是你不应该通过滴答事件来计算。您应该只显示自时钟开始时间以来经过的时间。
猜你喜欢
  • 2011-12-20
  • 2018-06-05
  • 2019-04-29
  • 2022-06-11
  • 2018-08-05
  • 1970-01-01
  • 2019-09-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多