1

我有一个如下定义的计时器。计时器执行一个长时间运行的任务。我遇到的问题是,当计时器运行时,间隔再次过去,另一个计时器执行开始,即_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();
            }
        }
4

2 回答 2

3

Let's properly write this up. It is the Interval assignment that is getting you into trouble. A sample program that demonstrates the issue:

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");
    }
}

Output:

Tick
Tick
Tick
...

Remove the Interval assignment in the Elapsed event handler to see the difference.

So, in other words, even though the AutoReset property is false, just assigning the Interval property is enough to get the timer to restart. Big surprise of course, nobody ever sees that coming. And it plays havoc on your program because you assign the Interval property early, before you start doing the heavy lifting. So the Elapsed event will be raised again, on another thread, while your previous one hasn't completed running yet. That's a threading bug waiting to happen, particularly if your Interval value is too small. You'll need to assign it later, do so inside the finally block. Good thing that you made the Interval too small, very hard to diagnose later.

is this a c# bug ?

The C# language is off the hook, this is a .NET Framework bug. System.Timers.Timer is in general just not a very good class, it has the usability of a chainsaw without a safety switch. It was meant to make the System.Threading.Timer class more usable, in particular solving a problem with the habit of it being garbage collected too soon. One problem solved, but three new problems added. This goes back to .NET 1.0, a framework version with training wheels. They just can't fix it anymore, too much existing code will break. You really are better off with the System.Threading.Timer, just make sure to keep the object referenced in a variable. Like you already do.

于 2014-04-13T14:59:16.777 回答
0

2017 年更新:

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

await Task.Delay(TimeSpan.FromSeconds(x)); 
于 2017-07-28T03:57:37.880 回答