首页 > 解决方案 > System.Threading.Timer 无法正常工作

问题描述

我注意到计时器不正确。

这是一个非常简单的 C# 代码:它将每 1 分钟打印一次当前日期/时间。我的预期结果是:让它在下午 3:30 运行,然后我们将有:下午 3:31、下午 3:32、下午 3:33,...

但有时不会收到上述结果:有时是下午 3:31、下午 3:32、下午 3:34,...

所以它失去了1行。

谁能指出我有什么问题?

class Program
{
    static Timer m_Timer;
    static int countDown;

    static void Main(string[] args)
    {
        countDown = 60; 
        m_Timer = new Timer(TimerCallback, null, 0, 1000);

        while (true) { System.Threading.Thread.Sleep(10); };
    }

    static void TimerCallback(Object o)
    {
        countDown -= 1; 
        if (countDown <= 0)
        {
            Console.WriteLine(" ===>>>>>" + System.DateTime.Now.ToString());
            countDown = 60;
        }
        System.Threading.Thread.Sleep(10000); //long running code demo
    }
}

标签: c#timer

解决方案


System.Threading.Timer 在线程池中的线程上运行。您运行回调函数,该函数每 1 秒在池中的一个线程上运行,并使用睡眠将其阻塞 10 秒。根据您在某些时间点线程池中有多少线程,它们都可能被阻塞并等待,或者.NET 应该为您分配池中最大线程数的新线程。


从评论扩展答案。

每个功能都是独立的,它不会等到另一个处理完成。一个简单的任务是:每隔 1 分钟调用一个函数来做某事。就我而言,“做某事”是将局部变量保存到 SQL Server 中。这个过程快不慢。我将 1 个计时器用于许多功能,因为每个功能都在不同的周期中安排。例如,功能 1 每 1 分钟触发一次,功能 2 每 10 秒触发一次……这就是我使用 1 秒计时器的原因。

当我从最初的问题中阅读时,您的用例似乎更复杂。您有不同的任务并尝试实现某种调度程序。也许每个特定的任务都很快,但总而言之,一些运行可能会更长且阻塞。不确定这个逻辑是如何很好地实现的,但可能会有很多边缘情况,例如错过了一些运行等。

我将如何处理它?

  1. 如果调度程序可以更复杂,我不会尝试自己实现。我会选择现成的解决方案,例如Quartz.NET。他们考虑边缘情况并帮助在需要的情况下扩展集群并帮助配置。
  2. 在任何情况下,我都会重构更大的计划,让每个任务根据配置(自定义实现或 Quartz)作为更小的任务按其计划运行
  3. 我将首先通过引入一些队列在本地扩展您的任务“队列”,例如使用 ConcurrentQueue 或BlockingCollection或任何生产消费者来限制线程数,并且如果此类执行的性能在集群上的扩展性不好。通过这样做,您至少可以保证 N 个任务可以在本地调度和执行,并且超出的所有任务都可以排队。也许为任务设置一些优先级也会有所帮助,因为可能会错过一些执行,但有些执行必须按计划运行。

如果您很可能已经遇到线程问题,我怀疑从线程计时器执行其他线程或任务开始是一个好主意。

你的问题不在于 System.Threading.Timer,它的工作做得很好。您的用例更复杂。


推荐阅读