.NET 3.5 C#Bug与System.Timer System.ObjectDisposedException:无法访问已处置的对象

在我的Windows服务应用程序中,我经常使用计时器。 我只使用System.Timers。 我之前从未遇到过这个问题,但突然间我得到了这个例外:

System.ObjectDisposedException: Cannot access a disposed object. at System.Threading.TimerBase.ChangeTimer(UInt32 dueTime, UInt32 period) at System.Threading.Timer.Change(Int32 dueTime, Int32 period) at System.Timers.Timer.UpdateTimer() at System.Timers.Timer.set_Interval(Double value) at MyApp.MySpace.MySpace2.MyClassWithTimer.MethodChangeTimerInterval() 

在我的方法中,我停止计时器,并更改计时器间隔。 那是我得到例外的地方。

我已经阅读了一些关于这个bug的内容但是即使在.NET 3.5中也存在这个bug仍然是可行的吗?

我如何解决它? 我应该在停止后更新计时器对象并将间隔设置为新对象吗? 我正在使用GC.KeepAlive(dataTimer);

编辑:我发现了一些关于这个问题的其他问题:

*我找到了一个链接http://www.kbalertz.com/kb_842793.aspx基本上,一旦停止计时器,内部System.Threading.Timer就可用于垃圾收集,有时会导致经过的事件不发生,或者有时导致处置引用exception。 虽然没有在文章中描述,但我的解决方案是每次停止计时器时创建一个新计时器并重新添加已用事件。 不高效但容易,对我而言不是处理器问题。 这完全解决了我的问题。 为所有回复的人喝彩。*

但我很困惑为什么这个bug仍然存在,我需要确保重新添加计时器是一个好主意…

导致错误的代码:

 private void StartAsyncResponseTimer() { switch (_lastRequestType) { case 1: asyncResponseTimer.Interval = 1000; break; case 2: asyncResponseTimer.Interval = 2000; break; case 3: asyncResponseTimer.Interval = 3000; break; default: asyncResponseTimer.Interval = 10000; break; } asyncResponseTimer.Start(); } 

从SerialPortDataReceived事件调用函数:

 private void SerialPortDataReceived(object sender, EventArgs e) { StartAsyncResponseTimer(); } 

在调用更改间隔之前停止计时器。

Timer是我class级的私人领域:

  private Timer asyncResponseTimer = new Timer(); 

编辑:该应用程序连续运行了几个月,这是我第一次遇到这个exception!

我的处置模式:

  public class SerialPortCommunication{ ... private void SerialPortDataReceived(object sender, EventArgs e) { ReadResponse(); StartAsyncResponseTimer(); } //used to determine if is recieving response over private void StartAsyncResponseTimer() { switch (_lastRequestType) { case 1: asyncResponseTimer.Interval = 1000; break; case 2: asyncResponseTimer.Interval = 2000; break; case 3: asyncResponseTimer.Interval = 3000; break; default: asyncResponseTimer.Interval = 10000; break; } asyncResponseTimer.Start(); } public virtual void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { // Dispose managed resources. } // Dispose unmanaged resources. _disposed = true; Stop(); } } ~SomeClass() { Dispose(false); } #endregion public void Stop() { _asyncResponseTimer.Stop(); serialPortManager.ClosePort(); } } 

您可以在处理完计时器之后获取串口数据吗? 这是你发布的数据唯一让我想到的东西! 你在Stop()做什么! Dispose()里面的方法????

当您在串行端口上接收数据时,似乎启动了计时器。 如果在定时器发送响应之前在端口上收到其他数据,会发生什么? 根据您发布的信息,似乎计时器间隔将在计时器仍在处理其计时器事件时更改并启动(再次)。

您考虑过上述情况吗? 是否自动重置计时器? 你什么时候打电话给Stop()?

由于您没有指定服务器配置,我假设Windows Server 2003.当您提到它(差不多)两个月时,它会提醒49.7天的错误 。 它可能不适用于您的方案,除非它在3月再次崩溃。 🙂

症状2
在未运行ISA Server的基于Windows Server 2003的计算机上,如果满足下列条件,则会发生类似的问题:在应用程序中重复调用CreateTimerQueueTimer函数。 您可以设置指定的时间段来触发CreateTimerQueueTimer函数创建的计时器。

该应用程序运行超过49.7天。 49.7天后,立即触发计时器,而不是在指定时间段后触发。 几分钟后,计时器被正确触发。

KeepAlive()在这里没用,因为你在类级别声明了计时器 – 这很好。 没有必要停止计时器来改变间隔。

从调用堆栈和您提供的代码,看起来您正在尝试更改已经处置的计时器的间隔。 您可能需要在确认没有其他工作要做之后停止计时器,而不是在您的配置模式中停止计时器,这似乎会干扰正在进行的工作。 也就是说,首先检查是否还有工作要做,如果没有,请停止监听端口,然后停止计时器。 除非我能看到代码的更多部分 – 比如事件处理程序,serialPortManager声明等,否则它不会有太大帮助。