我如何优雅地停止System.Threading.Timer?

我有一个用C#实现的Windows服务,需要经常做一些工作。 我使用System.Threading.Timer实现了这个,它带有一个回调方法,负责安排下一个回调。 我无法正常停止(即处理)计时器。 这是一些简化的代码,您可以在控制台应用程序中运行,以说明我的问题:

 const int tickInterval = 1000; // one second timer = new Timer( state => { // simulate some work that takes ten seconds Thread.Sleep( tickInterval * 10 ); // when the work is done, schedule the next callback in one second timer.Change( tickInterval, Timeout.Infinite ); }, null, tickInterval, // first callback in one second Timeout.Infinite ); // simulate the Windows Service happily running for a while before the user tells it to stop Thread.Sleep( tickInterval * 3 ); // try to gracefully dispose the timer while a callback is in progress var waitHandle = new ManualResetEvent( false ); timer.Dispose( waitHandle ); waitHandle.WaitOne(); 

问题是,当waitHandle.WaitOne阻塞时,我从回调线程上的timer.Change得到一个ObjectDisposedException 。 我究竟做错了什么?

我正在使用的Dispose重载文档说:

在完成所有当前排队的回调之前,不会释放计时器。

编辑:似乎文档中的此声明可能不正确。 有人可以validation吗?

我知道我可以通过在回调和处理代码之间添加一些信号来解决这个问题,正如Henk Holterman在下面提到的那样,但除非绝对必要,否则我不想这样做。

有了这段代码

  timer = new Timer( state => { // simulate some work that takes ten seconds Thread.Sleep( tickInterval * 10 ); // when the work is done, schedule the next callback in one second timer.Change( tickInterval, Timeout.Infinite ); }, null, tickInterval, // first callback in one second Timeout.Infinite ); 

几乎可以肯定的是,你会在睡觉时处理掉计时器。

您必须在Sleep()之后保护代码以检测Disposed计时器。 由于没有IsDisposed属性,因此快速且脏的static bool stopping = false; 可能会做的伎俩。

保护回调方法不能处理已处理的计时器的可能解决方案:

https://stackoverflow.com/a/15902261/193178

如“Windows上的并发编程”中所述:
创建一个虚拟类InvalidWaitHandle,inheritance自WaitHandle:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Threading; namespace MyNameSpace { class InvalidWaitHandle : WaitHandle { } } 

因此,您可以像这样正确配置System.Threading.Timer:

 public static void DisposeTimer() { MyTimer.Dispose(new InvalidWaitHandle()); MyTimer = null; } 

您不需要丢弃计时器来停止它。 您可以调用Timer.Stop()或将Timer.Enabled设置为false ,其中任何一个都将停止计时器运行。