我有一个如下定义的计时器。计时器执行长时间运行的任务。我遇到的问题是,当计时器运行时,间隔已经过了,另一个计时器执行开始,即_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();
}
}
答案 0 :(得分:3)
让我们正确地写下来。正是Interval任务让你陷入困境。一个演示该问题的示例程序:
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 块中执行此操作。你把Interval做得太小,以后很难诊断的好事。
这是一个c#bug吗?
C#语言已经摆脱困境,这是一个.NET Framework错误。 System.Timers.Timer通常不是一个非常好的类,它具有没有安全开关的电锯的可用性。它的目的是使System.Threading.Timer类更加可用,特别是解决了过早收集垃圾的习惯问题。一个问题解决了,但增加了三个新问题。这可以追溯到.NET 1.0,这是一个带有训练轮的框架版本。他们再也无法解决它,现有的代码太多会破坏。你最好使用System.Threading.Timer,只需确保在变量中保留引用的对象。就像你已经做的那样。
答案 1 :(得分:0)
2017年更新:
有一种新工具可以解决这些问题。
await Task.Delay(TimeSpan.FromSeconds(x));