【问题标题】:Synchronizing Forms.Timer and Diagnostics.Stopwatch同步 Forms.Timer 和 Diagnostics.Stopwatch
【发布时间】:2012-01-11 15:50:24
【问题描述】:

我有一个函数(比如foo()),它会不时以可变的时间间隔被调用。当它被调用时,它会检查时间并采取相应的行动。

我是通过以下方式完成的:

  • Forms.Timer 对象在需要时调用函数

  • 在函数中使用Diagnostics.Stopwatch 对象来确定时间并决定做什么。

但是我有以下问题:当foo()被Timer的回调调用时,秒表对象的ElapsedMilliseconds值通常低于预期。例如,计时器设置为 1000,因此在 1000 毫秒后调用 foo(),但在 foo() 主体内,ElapsedMilliseconds 返回 900,因此 foo 的行为就像经过的时间为 900(尽管它应该采取操作 A,因为实际过去了 1000 毫秒,它没有)

如果 ElapsedMilliseconds 与计时器的值一致,如何同步计时器和秒表?

编辑:一些代码

一些示例代码来解释我的问题:

//foo is the function that is called by timer's callback
public void foo()
{
    //Let's see what time it is: 
    long currentTime = stopwatch.ElapsedMilliseconds();
    Item = getCurrentItem(currentTime);
    Item.Draw();
}

//this is the callback of timer
private void timer1_Tick(object sender, EventArgs e)
    {
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }

这应该在每次间隔完成时绘制其他内容,但是由于ElapsedMilliseconds 返回的值比计时器声称的更早,虽然间隔结束,但不会绘制下一项

【问题讨论】:

  • 没有办法同步。
  • 你想完成什么?准确的时间?使用Windows.Forms.Timer 很难做到这一点。

标签: c# .net timer synchronization stopwatch


【解决方案1】:

你会得到很大的不同,因为你在 1/64 秒间隔内的某个地方启动了计时器。你会得到更好的结果:

    private void StartTimers() {
        int tick = Environment.TickCount;
        while (Environment.TickCount == tick) Thread.Sleep(0);
        timer1.Enabled = true;
        stopwatch.Start();
    }

while() 循环提高了计时器在 1/64 计时器滴答开始时启动的几率。只是改善,没有保证。而且你不能对 Tick 事件延迟触发做任何事情,这完全取决于你的 UI 线程的响应能力。然而总是迟到。不要使用此代码,编写您的代码,这样您就不会关心这些计时器是否不同步。您可能必须减少计时器的 Interval 才能完成此操作,问题并不清楚。

【讨论】:

  • +1 对于Environment.TickCountStopwatch 在这种情况下是多余的。
【解决方案2】:

使用这种方法您不会取得太大的成功。您不是在完全相同的时间启动每个计时器,也不是在完全相同的时间检查它们(在 Timer 触发它的事件和您的代码查询秒表之间有一些时间流逝)。

如果您希望事情保持同步,请选择一个计时器并以此为基础。例如,如果你想使用 Forms.Timer,在你的事件处理程序中只增加一个计数器变量 - 这将告诉你你的处理程序被调用了多少次,以及 Forms.Timer 说的有效时间已通过。这是一个示例(我将留给您处理计时器滴答时间足够长以至于计数器超过 long.MaxValue 的情况)

public void foo()
{
    Item = getCurrentItem(totalElapsed);
    Item.Draw();
}

long totalElapsed = 0;

private void timer1_Tick(object sender, EventArgs e)
    {
        totalElapsed += timer1.Interval;
        //set the timer for next time
        timer1.Interval = Intervals[periodIdx++];
        foo();
    }

【讨论】:

  • 如果经过一段时间并且 ElapsedTime 大于计时器甚至被触发时的时间,那也没关系。但是它更小,这就是问题所在。
  • 这只是意味着秒表在计时器之后启动。你永远不会让这两件事完全同步
  • ps...您可能希望将 XNA 视为替代 UI 框架。它使用了一种非常不同的范例,更适合于在精确的屏幕增量上更改 UI 等任务。而不是这个计时器混乱,你会给XNA一个Draw方法,它每秒会调用几次。为您的 Draw 方法提供的参数之一是您的代码应用程序运行了多长时间
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多