多个计时器已过问题

Posted

技术标签:

【中文标题】多个计时器已过问题【英文标题】:Multiple timer elapsed issue 【发布时间】:2014-05-27 10:44:21 【问题描述】:

我有一个如下定义的计时器。计时器执行一个长时间运行的任务。我遇到的问题是当计时器运行时,间隔再次过去,另一个计时器执行开始,即_timer_Elapsed。定时器完成后如何让定时器执行一个间隔。现在的方式可能会同时执行多个计时器,这会导致我的代码出现各种问题。

protected override void OnStart(string[] args)
        

           _timer = new System.Timers.Timer();
           _timer.AutoReset = false;
           _timer.Interval = (Convert.ToInt32(ConfigurationManager.AppSettings["CheckInterval"]));
           _timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
           _timer.Enabled = true;
           _timer.Start(); // Start timer 

        


 public static void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        
            try
            
                _timer.Interval = (Convert.ToInt32(ConfigurationManager.AppSettings["CheckInterval"])); 

                BLLGenericResult SystemCheckItems_Result = ServiceItemOperations.CheckItems(); // Long Running Task

            
            catch (Exception ex)
            
                // Exception handling...
            
            finally
            
                _timer.Start();
            
        

【问题讨论】:

这是不可能的,使用 AutoReset = false 确保计时器不会再次触发,除非您明确重新启用它。您很可能会以错误的方式进行操作,例如在 Elapsed 事件处理程序的开头而不是结尾重新启用它。或者没有正确实现 OnStop() 方法。 @HansPassant 是的 - 我也在读这个 - 让我提出我的 _Timer_Elapsed 方法,看看你的想法 或者...您再次调用了 OnStart 函数,并且除了前一个仍在运行的计时器之外,还启动了一个新计时器。 @SteveWellens 我不这么认为 - OnStart 只调用一次 - 在 Windows 服务启动时 将间隔分配也移到底部。又一个 System.Timers.Timer 怪癖。 【参考方案1】:

让我们正确地写下来。给你带来麻烦的是间隔分配。演示该问题的示例程序:

using System;

class Program 
    static System.Timers.Timer timer;
    static void Main(string[] args) 
        timer = new System.Timers.Timer();
        timer.AutoReset = false;
        timer.Interval = 1000;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
        Console.ReadLine();
    

    static void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) 
        timer.Interval = 1000;
        Console.WriteLine("Tick");
    

输出:

Tick
Tick
Tick
...

删除 Elapsed 事件处理程序中的 Interval 分配以查看差异。

因此,换句话说,即使 AutoReset 属性为 false,仅分配 Interval 属性就足以让计时器重新启动。当然,大惊喜,没有人预见到这一点。而且它会对您的程序造成严重破坏,因为您提前分配了 Interval 属性,开始执行繁重的工作之前。因此,Elapsed 事件将在另一个线程上再次引发,而您之前的事件尚未完成运行。这是一个等待发生的线程错误,特别是如果您的 Interval 值太小。您需要稍后分配它,在 finally 块内进行。幸好您将间隔设置得太小,以后很难诊断。

这是一个 c# 错误吗?

C# 语言已摆脱困境,这是一个 .NET Framework 错误。 System.Timers.Timer 通常不是一个很好的类,它具有没有安全开关的电锯的可用性。它旨在使 System.Threading.Timer 类更有用,特别是解决过早收集垃圾的习惯问题。解决了一个问题,但又增加了三个新问题。这可以追溯到 .NET 1.0,这是一个带有训练轮的框架版本。他们只是无法再修复它,太多现有代码会破坏。使用 System.Threading.Timer 确实更好,只需确保将引用的对象保留在变量中。就像你已经做的那样。

【讨论】:

这里的细节令人印象深刻——非常感谢。【参考方案2】:

2017 年更新:

有一种新工具可以用来解决这类问题。

await Task.Delay(TimeSpan.FromSeconds(x)); 

【讨论】:

以上是关于多个计时器已过问题的主要内容,如果未能解决你的问题,请参考以下文章

如何知道计时器是不是已在 C 中结束

Sqlserver2012 评估期已过问题

sqlserver2012评估期已过问题处理

Sqlserver2012评估期已过问题解决

sql server 2008 R2 评估期已过问题,不能解决

编程题:计时器