停止时同步Timers.Timer经过的方法

参考MSDN关于System.Timers.Timer的这句话:

Timer.Elapsed事件是在ThreadPool线程上引发的,因此事件处理方法可能在一个线程上运行,同时调用Timer.Stop方法在另一个线程上运行。 这可能导致在调用Stop方法后引发Elapsed事件。 通过将SignalTime属性与调用Stop方法的时间进行比较,无法阻止此竞争条件,因为事件处理方法可能在调用Stop方法时已经执行,或者可能在Stop方法之间开始执行被调用以及保存停止时间的时刻。 如果在事件处理方法仍在执行时阻止调用Stop方法的线程继续运行至关重要,请使用更强大的同步机制,例如Monitor类或CompareExchange方法。 使用CompareExchange方法的代码可以在Timer.Stop方法的示例中找到。

任何人都可以举一个“ 强大的同步机制,如Monitor类 ”来解释这究竟意味着什么?

我认为这意味着以某种方式使用锁,但我不确定如何实现它。

可靠地停止System.Timers.Timer确实是一项重大工作。 最严重的问题是它用于调用Elapsed事件的线程池线程可以由于线程池调度程序算法而备份。 进行几次备份呼叫并不罕见,技术上有数百种可能。

您需要两个同步,一个用于确保仅在没有运行Elapsed事件处理程序时停止计时器,另一个用于确保这些备份的TP线程不会造成任何伤害。 像这样:

System.Timers.Timer timer = new System.Timers.Timer(); object locker = new object(); ManualResetEvent timerDead = new ManualResetEvent(false); private void Timer_Elapsed(object sender, ElapsedEventArgs e) { lock (locker) { if (timerDead.WaitOne(0)) return; // etc... } } private void StopTimer() { lock (locker) { timerDead.Set(); timer.Stop(); } } 

考虑将AutoReset属性设置为false。 这是另一种方式,Elapsed事件从一个捕获Exception的内部.NET方法调用。 非常讨厌,你的计时器代码停止运行,根本没有任何诊断。 我不知道历史,但是MSFT的其他团队肯定已经喘不过气来,并写下了System.Threading.Timer。 强烈推荐。

这就是它的建议。

Monitor是C#编译器用于lock语句的类。

话虽如此,如果在您的情况下这是一个问题,上述只是一个问题。 整个语句基本上都转换为“你可以在调用Stop()之后立即获得一个计时器事件。如果这是一个问题,你需要处理它。” 根据您的计时器正在做什么,这可能是一个问题,或者可能不是。

如果这是一个问题, Timer.Stop页面会显示一种强大的方式(使用Interlocked.CompareExchange )来处理这个问题。 只需从示例中复制代码并根据需要进行修改。

尝试:

 lock(timer) { timer.Stop(); } 

这是一种防止这种竞争条件发生的非常简单的方法:

 private object _lock = new object(); private Timer _timer; // init somewhere else public void StopTheTimer() { lock (_lock) { _timer.Stop(); } } void elapsed(...) { lock (_lock) { if (_timer.Enabled) // prevent event after Stop() is called { // do whatever you do in the timer event } } } 

似乎计时器不是线程安全的。 您必须通过锁定保持对它的所有呼叫同步。 lock(object){}实际上只是一个简单的监视器调用的简写。