【问题标题】:Why is my timer firing early?为什么我的计时器提前触发?
【发布时间】:2017-06-22 20:13:47
【问题描述】:

我试图精确地每分钟触发一个方法。

我正在使用下面的代码,但我注意到计时器可能会提前几毫秒触发。

我该如何解决这个问题?

    public partial class Form1 : Form
    {

        DateTime RoundUpFuzzy(DateTime dt, TimeSpan d)
        {
            int n=1;
            if (dt.Millisecond > 900)
                n = 2;
            return new DateTime(((dt.Ticks + (d.Ticks*n) - 1) / d.Ticks) * d.Ticks);
        }

        public void HistMatch()
        {
            for (int i = 0; i < 10; i++)
            {
                var now = DateTime.UtcNow;
                var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
                var waitTS = nextDT.Subtract(now);
                Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff")+" wait MS: " + waitTS.TotalMilliseconds);
                System.Threading.Thread.Sleep(waitTS);
            }
        }
}

输出:

Now: 09:12:16.308 nextDT: 09:13:00.000 waitMS: 43691.7256
Now: 09:12:59.999 nextDT: 09:14:00.000 waitMS: 60000.0952
Now: 09:13:59.990 nextDT: 09:15:00.000 waitMS: 60009.0949
Now: 09:14:59.992 nextDT: 09:16:00.000 waitMS: 60007.4955
Now: 09:15:59.993 nextDT: 09:17:00.000 waitMS: 60006.2285
Now: 09:16:59.996 nextDT: 09:18:00.000 waitMS: 60003.2285
Now: 09:17:59.996 nextDT: 09:19:00.000 waitMS: 60003.2285
Now: 09:18:59.996 nextDT: 09:20:00.000 waitMS: 60003.2285

编辑 1 - 附加测试代码

在这里,我正在尝试 3 个版本来准确地每分钟运行一个事件。只有第一个“Test_Sleep”是准确的。我怎样才能获得相同的结果但使用计时器?

public partial class Form1 : Form
{
    System.Threading.Timer threadingTimer;
    System.Timers.Timer timersTimer;

    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        Test_Sleep();
        Test_ThreadingTimer("");
        Test_TimersTimer("", null);
    }

    public void Test_TimersTimer(object o, System.Timers.ElapsedEventArgs e)
    {
        var now = DateTime.UtcNow;
        var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
        var ms = nextDT.Subtract(now);
        Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + ms.TotalMilliseconds);
        timersTimer = new System.Timers.Timer();
        timersTimer.Interval = (int)ms.TotalMilliseconds;
        timersTimer.Elapsed += Test_TimersTimer;
        timersTimer.AutoReset = false;
        timersTimer.Enabled = true;
    }

    public void Test_ThreadingTimer(object o)
    {
        var now = DateTime.UtcNow;
        var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
        var ms = nextDT.Subtract(now);
        Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + ms.TotalMilliseconds);
        threadingTimer = new System.Threading.Timer(new System.Threading.TimerCallback(Test_ThreadingTimer), null, ms, TimeSpan.FromMilliseconds(-1));
    }

    public void Test_Sleep()
    {
        for (int i = 0; i < 10; i++)
        {
            var now = DateTime.UtcNow;
            var nextDT = RoundUpFuzzy(now, TimeSpan.FromMinutes(1));
            var waitTS = nextDT.Subtract(now);
            Console.WriteLine("Now: " + now.ToString("HH:mm:ss.fff") + " nextDT: " + nextDT.ToString("HH:mm:ss.fff") + " waitMS: " + waitTS.TotalMilliseconds);
            System.Threading.Thread.Sleep(waitTS);
        }
    }

    DateTime RoundUpFuzzy(DateTime dt, TimeSpan d)
    {
        int n = 1;
        if (dt.Millisecond > 900)
            n = 2;
        return new DateTime(((dt.Ticks + (d.Ticks * n) - 1) / d.Ticks) * d.Ticks);
    }
}

【问题讨论】:

  • 检查实际时间,如果几毫秒后还没有到期:添加另一个计时器回调?或者如果它真的很接近,某种旋转等待?请注意,计时精度并不准确;计时器或时钟,或两者兼而有之:很容易关闭几毫秒
  • 你用什么定时器?
  • @MartinMulder 你有完整的代码:我正在使用睡眠。
  • 你的RoundUpFuzzy方法的目的是什么?
  • @MongZhu To RoundTime to Next 精确到 1 分钟,除非我们距离它已经不到 100 毫秒。在这种情况下,四舍五入到更远的下一分钟。

标签: c# timer


【解决方案1】:

Threading 和 Timer 有一个 resolution of around 15 ms,所以你不能期待比这更好的了...

对于精确的时间测量,您必须使用StopWatch class from the .Net,因为这是基于高分辨率频率。

【讨论】:

  • 过去是这样,但不再是这样了。某些现代 Windows 操作系统不再存在该问题。
  • @ManInMoon 你有提供信息的链接吗?因为我在 MSDN 上只能找到 15 毫秒的分辨率,我有兴趣阅读它。
  • @ManInMoon 它明确指出某些硬件可以管理它,但是您需要添加代码片段来强制整个系统使用这个特殊的时钟。所以通过使用 System.trheading.thread.sleep,你仍然停留在 15 毫秒
  • 马丁。你是对的。在我的真实世界应用程序中,我实际上使用的是 system.threading.thread.sleep。这是非常准确的。查看已编辑的问题
【解决方案2】:

为了避免这些问题,我推荐 Quartz.Net (https://www.quartz-scheduler.net/)。使用方便

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 2017-11-02
    • 2022-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多