只有在前一个事件完成时才提出事件

我在我的应用程序中使用System.Timers.Timer 。 每一秒我都会运行一个能完成某项工作的function。 问题是,这个函数可以阻塞一段时间(它读取然后从磁盘处理一个大文件)。 我想只在前一个“执行实例”完成后启动该function。 我以为我可以用Mutex实现这个目标:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Timers; using System.Threading; namespace ConsoleApplication1 { class Program { static Mutex TimerMut = new Mutex(false); public static void Main() { Thread TT = new Thread(new ThreadStart(delegate() { System.Timers.Timer oTimer = new System.Timers.Timer(); oTimer.Elapsed += new ElapsedEventHandler(Handler); oTimer.Interval = 1000; oTimer.Enabled = true; })); TT.Start(); Console.Read(); } private static void Handler(object oSource, ElapsedEventArgs oElapsedEventArgs) { TimerMut.WaitOne(); Console.WriteLine("foo"); Thread.Sleep(500); //simulate some work Console.WriteLine("bar"); TimerMut.ReleaseMutex(); } } } 

这不起作用,“foos”仍然每秒出现。 我怎样才能做到这一点?

编辑:你是对的,开始一个新的线程来处理它是没有意义的。 我以为只有System.Threading.Timer在一个单独的线程中启动。

我知道最容易做到这一点的方式:

 internal static volatile bool isRunning; public static void Main() { Thread TT = new Thread(new ThreadStart(delegate() { System.Timers.Timer oTimer = new System.Timers.Timer(); oTimer.Elapsed += new ElapsedEventHandler(Handler); oTimer.Interval = 1000; oTimer.Enabled = true; })); TT.Start(); } private void Handler(object oSource, ElapsedEventArgs oElapsedEventArgs) { if(isRunning) return; isRunning = true; try { Console.WriteLine("foo"); Thread.Sleep(500); //simulate some work Console.WriteLine("bar"); } finally { isRunning = false; } } 

处理程序仍然运行,但它首先要做的是确保另一个处理程序没有运行,如果是,它会立即停止。

对于更快速执行处理程序的计时器(例如每秒3-4次),这有可能比赛; 在其中一个线程设置该位之前,两个线程可以继续通过保护子句。 您可以使用几个lock语句来避免这种情况,类似于Mutex或Monitor:

  static object syncObj = new object(); private void Handler(object oSource, ElapsedEventArgs oElapsedEventArgs) { lock(syncObj) { if(isRunning) return; isRunning = true; } try { Console.WriteLine("foo"); Thread.Sleep(500); //simulate some work Console.WriteLine("bar"); } finally { lock(syncObj) { isRunning = false; } } } 

这将确保只有一个线程可以检查或修改isRunning,并且因为isRunning被标记为volatile,所以CLR不会将其值缓存为每个线程的性能状态的一部分; 每个线程必须查看完全相同的内存位置以检查或更改该值。

如果你想跳过勾选,如果另一个已经工作,你可以这样做。

 private readonly object padlock = new object(); private void SomeMethod() { if(!Monitor.TryEnter(padlock)) return; try { //Do heavy work } finally { Monitor.Exit(padlock); } } 

如果此方法的另一个调用仍在运行,则可以按照以下模式跳过执行指定的工作:

 private int isWorking = 0; public void Foo() { if (Interlocked.Exchange(ref isWorking, 1) == 0) { try { //Do work } finally { Interlocked.Exchange(ref isWorking, 0); } } } 

您使用Mutex将导致额外的刻度等待较早的刻度完成 ,而不是在另一个仍在运行时跳过调用 ,这就是您所说的。 (当处理这样的计时器时,常见的是想要跳过这样的刻度,而不是等待。如果你的刻度处理程序经常花费太长时间,你会得到一个等待处理程序的巨大队列。)

我不确定你为什么要使用新的线程来启动计时器,因为计时器在他们自己的线程上运行,但这是一个有效的方法。 只需关闭定时器,直到完成当前间隔。

 static System.Timers.Timer oTimer public static void Main() { oTimer = new System.Timers.Timer(); oTimer.Elapsed += new ElapsedEventHandler(Handler); oTimer.Interval = 1000; oTimer.Enabled = true; } private void Handler(object oSource, ElapsedEventArgs oElapsedEventArgs) { oTimer.Enabled = false; Console.WriteLine("foo"); Thread.Sleep(5000); //simulate some work Console.WriteLine("bar"); oTimer.Enabled = true; }