如何防止System.Timers.Timer排队在线程池上执行?

标准System.Timers.Timer行为存在问题。 计时器以某个间隔提升Elapsed事件。 但是当Elapsed事件处理程序内的执行时间超过计时器间隔时,线程池开始排队事件处理。 这是我的问题。 这是因为使用我的Elapsed事件处理程序,我从数据库中获取一些数据并使用它做一些事情,最后将结果保存回数据库。 但是数据处理应该只提供一次。 那么,有没有办法防止排队System.Timers.Timer的elapse事件。

作为此问题的插图,您可以考虑下一个测试程序:

public class EntryPoint { private static void TimeProc(object state, ElapsedEventArgs e) { Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(20000); } static void Main(string[] args) { Console.WriteLine("Press  for finishing\n\n"); ThreadPool.SetMaxThreads(10, 10); System.Timers.Timer MyTimer = new System.Timers.Timer(1000); MyTimer.Elapsed += new ElapsedEventHandler(TimeProc); MyTimer.Start(); Console.ReadLine(); MyTimer.Stop(); } } 

可能的输出将如下:

 Current time 03.02.2011 0:00:09 on the thread 4 Current time 03.02.2011 0:00:10 on the thread 5 Current time 03.02.2011 0:00:12 on the thread 6 Current time 03.02.2011 0:00:13 on the thread 7 Current time 03.02.2011 0:00:14 on the thread 8 Current time 03.02.2011 0:00:15 on the thread 9 Current time 03.02.2011 0:00:16 on the thread 10 Current time 03.02.2011 0:00:17 on the thread 11 Current time 03.02.2011 0:00:18 on the thread 12 Current time 03.02.2011 0:00:19 on the thread 13 Current time 03.02.2011 0:00:30 on the thread 4 Current time 03.02.2011 0:00:30 on the thread 5 

可能的解决方案:

1)它的灵感来自: C#Timer vs Thread in Service

并且有关于上述示例的代码如下:

  public class EntryPoint { private static System.Timers.Timer MyTimer; private static void TimeProc(object state, ElapsedEventArgs e) { Console.WriteLine("Current time {0} on the thread {1}", DateTime.Now, Thread.CurrentThread.ManagedThreadId); Thread.Sleep(20000); MyTimer.Enabled = true; } static void Main(string[] args) { Console.WriteLine("Press  for finishing\n\n"); ThreadPool.SetMaxThreads(10, 10); MyTimer = new System.Timers.Timer(1000); MyTimer.AutoReset = false; MyTimer.Elapsed += new ElapsedEventHandler(TimeProc); MyTimer.Enabled = true; Console.ReadLine(); } } 

2)第二种方式是关于SynchronizingObject,但它仅对Windows表单应用程序有价值,或者需要额外开发代码来实现将实现ISynchronizeInvoke接口的对象。 更多关于这种方式,你可以在这里找到

所以,现在我更喜欢第一个解决方案。

在这种情况下我通常做的是在Elapsed处理程序的开始处停止计时器并在结束时再次启动它。 这样,您一次只能处理一个刻度。

更新:

根据MSDN链接,我认为他们的意思是你可以设置自己的标志(但仍然有勾号),但也应采取线程安全措施。

我会说简单地停止它,然后在这么长的执行后启动它。

 tmr.Stop(); //Your lengthy execution code goes here tmr.Start(); 

您看到的行为是设计的。 在计时器上设置SynchronizingObject ,或者使用另一个不在多个线程上打勾的计时器(例如System.Threading.Timer )。

我只是创建一个静态标志变量。 这样我的计时器就会继续运行,但如果方法在下一个计时器周期之前没有完成,则只需绕过代码。

在用于计时器的方法中,测试事件是否正在进行中。

 Timer_Method_Called() { if (eventInProgress == 0) { // flag event as in progress eventInProcess == 1; // perform code.... // after code is complete, allow the method to execute eventInProgress == 0; } } 

由于没有一个答案是线程安全的,让我提出一个:

 void oneHundredMS_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (setTimerBodyRunning()) { //only proceed to body if it is not already processing; setTimerBodyRunning must be thread-safe // here you do your long running operation setTimerBodyFinished(); } } 

如您所见,计时器处理程序首先检查它是否尚未运行,并且只有在返回false时才进入正文。 如果返回true,则处理程序快速返回并且ticks不排队(他们将使用简单的锁定语句)。 以下是setTimerBodyRunning和setTimerBodyFinished的定义:

 private bool setTimerBodyRunning() { bool retVal = false; lock (timerBodyRunning) { //timerBodyRunning is type object and it holds a bool. //The reason it is object and not bool is so it can be locked on to ensure thread safety if (!((bool)timerBodyRunning)) { timerBodyRunning = true; retVal = true; } } return retVal; } private void setTimerBodyFinished() { lock (timerBodyRunning) { timerBodyRunning = false; } } 

以下是初始化和启动计时器的方法:

 object timerBodyRunning = new object(); timerBodyRunning = false; System.Timers.Timer timerFrequency100MS = new System.Timers.Timer(); timerFrequency100MS.Interval = FREQUENCY_MS; //it will fire every 100 milliseconds timerFrequency100MS.Elapsed += new System.Timers.ElapsedEventHandler(oneHundredMS_Elapsed); timerFrequency100MS.Start();