简体   繁体   中英

.NET 3.5 C# Bug with System.Timer System.ObjectDisposedException: Cannot access a disposed object

In my Windows service app I am using timers a lot. I'm using only System.Timers. I've never experienced this problem before, but suddenly I got this exception:

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

In my method I am stopping the timer, and changing the timer interval. That is the place where I got the exception.

I have read something about this bug but is it still passible to have this bug even in .NET 3.5?

How do I fix it? Should I renew the timer object after stopping and set the interval to a new object? I am using GC.KeepAlive(dataTimer);

Edit: I found some other questions about this problem:

*I found a link http://www.kbalertz.com/kb_842793.aspx Basically as soon as you stop a timer, the internal System.Threading.Timer becomes available for Garbage Collection, sometimes causing the elapsed event not to occur, or sometimes causing a disposed reference exception. Although not described in the article, my solution was to create a new timer every time the timer was to be stopped and re-add the elapsed events. Not efficient but easy, and not a problem processor-wise to me. This has totally solved my problem. Cheers for all who responded.*

But I am confused as to why the bug is still there, and I need to be sure that re-adding the timer is a good idea...

Code that caused the error:

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

Function was called from SerialPortDataReceived event:

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

Timer was stopped before calling changing interval.

Timer is private field of my class:

  private Timer asyncResponseTimer = new Timer();

EDIT: The application has been running for several months in a row and this is first time I got this exception!

My dispose pattern:

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

Can it be that your getting serial port data just after you have disposed your timer? It is the only thing that comes to my mind with the data you have posted!. What are you doing in the Stop()! method inside the Dispose()????

It seems as you start your timer when receiving data on the serial port. What happens if you receive additional data on the port before the timer has finished sending the response? It seems, based on the information you have posted, that the timer interval then will be changed and started (again) while timer still is processing its timer event.

Have you considered the above scenario? Is the timer AutoReset or not? When are you calling Stop()?

Since you have not specified your server configuration, I am assuming Windows Server 2003. When you mention it worked for (almost) two months, it reminds of the 49.7 days bug . It may not be applicable to your scenario, unless it crashes again in March. :-)

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.

KeepAlive() is not useful here because you declared the timer at the class level - which is good. It is not necessary to stop the timer for changing the interval.

From the call stack and the code you have provided, it looks like you are trying to change the interval on the already disposed timer. You probably need to stop the timer after confirming there is no more work to do, instead of stopping it in your dispose pattern which seems to interfere with ongoing work. That is, first you check if there is anymore work to be done, if not, stop listening to the port and then stop the timer. It is not going to be of much help unless I can see some more parts of the code - like the event handlers, serialPortManager declaration etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM