【问题标题】:.NET 3.5 C# Bug with System.Timer System.ObjectDisposedException: Cannot access a disposed objectSystem.Timer System.ObjectDisposedException 的 .NET 3.5 C# 错误:无法访问已处置的对象
【发布时间】:2011-01-25 12:51:52
【问题描述】:

在我的 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()

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

我已经阅读了一些关于这个错误的信息,但是即使在 .NET 3.5 中也有这个错误是否仍然可以通过?

我该如何解决?我应该在停止后更新计时器对象并将间隔设置为新对象吗?我正在使用 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();

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

我的处置模式:

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

【问题讨论】:

  • 给我们您的代码以及错误信息。我的猜测是你正在使用一个“使用”块,当你不应该这样做时,它会在完成时自动处理一些东西。我们会查看您的代码并尝试告诉您。
  • 我在使用计时器时的价值在于在某处保留对它们的引用(例如私有字段或计时器集合),以便我知道仍有引用。这样我就知道他们不会在我想之前被 GCed。尽管您这样做了,但您当然必须确保在完成计时器对象后删除该引用。
  • 我有定时器被引用为类的私有成员。
  • 引用的错误很久以前就修复了。查看 Dispose() 调用和 using 语句的代码。区分代码的已知工作版本应该会有所帮助。
  • 创建一个新项目并构建一个重现错误并显示该代码的基本项目。

标签: c# .net exception timer


【解决方案1】:

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

【讨论】:

  • 我从不调用函数 timer.Dispose()。我需要 Stop() 来停止串行端口通信。我也有方法 Start()。当我处理 SerialPortCommunication 类时,我也会停止计时器以防止在处理后发生,因为它在不同的线程上。处置时未提出该例外。它发生在与串口通信的中间。每次发生 SerialPortDataReceived() 时都会调用重置计时器的时间间隔。
【解决方案2】:

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

您考虑过上述情况吗?定时器是否自动复位?你什么时候调用 Stop()?

【讨论】:

  • 计时器在那里确定是否没有收到数据。因此,每次发生 serialPortDataReceived 时都会重新启动计时器。如果计时器完成他的工作,我会知道所有数据都已收到,我可以继续发送请求。 Timer 有默认的 AutoReset=true。仅当我想停止串行端口通信时才调用 Stop(),并且当我遇到异常时 Stop() 未被调用。它基本上由用户从 UI 调用。
【解决方案3】:

由于您没有指定您的服务器配置,我假设是 Windows Server 2003。当您提到它(几乎)工作了两个月时,它提醒了 49.7 days bug。它可能不适用于您的场景,除非它在 ​​3 月再次崩溃。 :-)

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

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

KeepAlive() 在这里没有用,因为您在类级别声明了计时器 - 这很好。更改间隔无需停止计时器。

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

【讨论】:

    猜你喜欢
    • 2023-03-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多