2011-01-25 91 views
6

在我的Windows服务应用程序中,我使用了很多计时器。我只使用System.Timers。 我以前从来没有遇到过这个问题,但突然我得到这个异常:.NET 3.5 C#与System.Timer错误System.ObjectDisposedException:无法访问处置对象

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变为可用于垃圾收集, 有时会导致过期事件不发生,或者有时导致处置的参考异常 。 尽管在文章中没有描述,我的解决方案是 创建一个新的计时器,每当计时器停止并重新添加已过去的事件。效率不高,但容易, ,对我来说不是处理器方面的问题。 这完全解决了我的问题。 干杯所有谁回答。*

但我很困惑,为什么这个错误仍然存​​在,我需要,以确保重新添加定时器是一个好主意......

代码造成错误:

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(); 
} 

定时器在呼唤改变区间之前停止。

定时器是我的类的私有领域:

private Timer asyncResponseTimer = new Timer(); 

编辑:行中的应用程序已经运行了数月,这是我第一次得到这个例外!

我的Dispose模式:

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(); 
    } 
} 
+0

给我们您的代码以及错误消息。我的猜测是你正在使用一个“使用”块,当你不应该完成时,它会自动处理一些东西。我们会看看你的代码,尽量告诉你。 – Chris 2011-01-25 12:59:20

+2

使用计时器时,我的技术的价值在于在某处(例如私人字段或定时器集合)保留对它们的引用,以便我知道仍然有引用。这样,我知道他们不会在我想要之前被GCed。虽然你当然必须确保在完成计时器对象时删除该引用。 – Chris 2011-01-25 13:01:06

+0

我有定时器引用为类的私有成员。 – Simon 2011-01-25 13:07:29

回答

1

它可以是你得到你已经设置你的计时器只是后串口数据?这是唯一让我想起你发布的数据!你在Stop()做什么? Dispose()中的方法?

1

看起来像你在串口接收数据时开始计时。如果在定时器完成发送响应之前,您在端口上收到其他数据会发生什么情况?看起来,根据您发布的信息,定时器间隔将在定时器仍在处理定时器事件时更改并启动(再次)。

您是否考虑过上述情况?定时器AutoReset或不?你什么时候打电话给Stop()?

1

由于您尚未指定服务器配置,因此我假定使用Windows Server 2003.当您提及它工作了(几乎)两个月时,它会提醒您49.7 days bug。它可能不适用于你的情况,除非它在3月份再次崩溃。这是很好的 - :-)

Symptom 2
On a Windows Server 2003-based computer that is not running ISA Server, a similar problem occurs when the following conditions are true: You call the CreateTimerQueueTimer function repeatedly in an application. You set a specified period to trigger the timer that is created by the CreateTimerQueueTimer function.

The application runs for more than 49.7 days. After 49.7 days, the timer is triggered immediately instead of being triggered after the specified period. After several minutes, the timer is triggered correctly.

保持活动(),因为你宣布在类级别的计时器是不是在这里非常有用。没有必要停止改变间隔的定时器。

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