System.Threading.Timer最大dueTime值问题

时间:2010-08-19 10:40:00

标签: c# .net timer

According to the documentation最大dueTime值为4294967294毫秒。但是,如果将计时器设置为此值,则会立即触发回调。

运行以下程序并单击“c” - 回调将立即触发,单击“o”,它将在49天后触发,单击“v”将在1秒后触发。这是一个错误还是我做错了什么?

using System;
using System.Threading;

namespace Test
{
    class Program
    {
        private static bool isRunning;
        private static Timer t;

        static void Main(string[] args)
        {
            t = new Timer(OnTimerCallback, null, Timeout.Infinite, Timeout.Infinite);
            isRunning = true;
            while (isRunning)
            {
                var command = Console.ReadKey();

                switch (command.KeyChar)
                {
                    case 'q':
                        isRunning = false;
                        break;
                    case 'c':
                        t.Change(TimeSpan.FromMilliseconds(4294967294), TimeSpan.FromMilliseconds(-1));
                        break;
                    case 'o':
                        t.Change(TimeSpan.FromMilliseconds(4294000000), TimeSpan.FromMilliseconds(-1));
                        break;
                    case 'v':
                        t.Change(TimeSpan.FromMilliseconds(1000), TimeSpan.FromMilliseconds(-1));
                        break;
                }
            }
            Environment.Exit(0);
        }

        private static void OnTimerCallback(object x)
        {
            Console.WriteLine("Timer callback");
            t.Change(TimeSpan.FromMilliseconds(4294967294), TimeSpan.FromMilliseconds(-1));
        }
    }
}

更新

如果在回调中将timer设置为max dueTime值,它将不会立即触发并按预期工作:)

3 个答案:

答案 0 :(得分:2)

这看起来像是CLR实现计时器的竞赛。我需要在确切的原因上挥手,我真的不明白它怎么会出问题。至少从查看SSCLI20源代码(clr \ src \ vm \ win32threadpool.cpp)开始,使用当前发货代码的情况很可能不再准确。

我可以通过在Change()调用中设置断点来轻松地重现问题,但不能让代码在没有中断的情况下运行。它的行为类似于从GetTickCount()返回的最后一个观察值而不是当前值计算到期时间。将0xfffffffe添加到该陈旧值,然后计算已过期的时间。滴答计数仅具有32位分辨率。我认为真正的竞争在于计算SleepEx()等待下一次到期事件的时间,但这是猜测。

如果不使用调试器暂停计时器线程,也可能发生此竞争。虽然它可能很少见。我不得不建议你远离接近0xfffffffe的任何值,以确保不会发生这种情况。没有困难,24天睡觉的效率并不比睡眠时间长49天:)通过自己计算睡眠时间来实现更长的间隔时间。

答案 1 :(得分:1)

这实际上是一个有趣的问题。我试着在反射器中查看该方法,但结果是本地内部调用。

但你有什么理由想这么做吗?根据文档,您可以将dueTime更改为-1Timeout.Infinite以禁用它,就像您在构造函数中所做的那样。

您也可以使用:

t.Change(4294967294u, -1u);

而不是

t.Change(TimeSpan.FromMilliseconds(4294967294), TimeSpan.FromMilliseconds(-1));

答案 2 :(得分:1)

  

根据文档,最大dueTime值为4294967294毫秒。

4294967295为UIn32.MaxValue,因此4294967294按位转换为int为-2。

但我在文档中看不到任何明确表示此值正常的内容。列出0和-1。

[请参阅评论:这只是转换为UInt32]您是否考虑使用Timer.Changelong重载,这会产生更大的范围?

附加:查看SetWaitableTimer的文档(我知道System.Threading.Timer是基于Win32 Waitable Timers构建的)我看到了适当的时间参数:

  

负值表示相对时间。

因此-2可能意味着从现在起200ns开火,这基本上是立即的。