4

这是堆栈跟踪:

2012-03-16 19:15:09Z E System.NullReferenceException:对象引用未设置为对象的实例。
在 System.Timers.Timer.set_Enabled(布尔值)
在 System.Timers.Timer.Stop()

这是代码:

定时器声明为私有成员变量。

Private _myTimer As System.Timers.Timer

初始化定时器方法。

Private Sub InitializeMyTimer()

    _myTimer = New System.Timers.Timer

    _myTimer.Interval = My.Settings.TimeoutSeconds * 1000
    _myTimer.Start()

    AddHandler _myTimer.Elapsed, AddressOf MyTimer_Elapsed

End Sub

计时器经过的方法。WsMethodAsync 调用 .asmx Web 服务方法。

Private Sub MyTimer_Elapsed(ByVal sender As Object, ByVal e As     System.Timers.ElapsedEventArgs)

    Try

        _myTimer.Stop()

        Using thisWSHelper As New WSHelper

            thisWsHelp.WsMethodAsync()

        End Using

        _myTimer.Start()

    Catch ex As Exception

      LogAndShowException(ex)

    End Try

End Sub

计时器必须有一个值,否则 Timer.Stop() 调用将引发异常。这是一个零星的错误,我只是想看看是否有人以前经历过这种情况,或者是否有人对可能导致它的原因有任何想法。它发生在 WinForms 应用程序中 Timer 的 Elapsed 事件的事件处理程序中,但它只是偶尔在用户计算机上发生。我自己无法重现该错误。

4

2 回答 2

2

好吧,让我们假设您意识到 System.Timers.Timer 类实现了 IDisposable 并且您编写了代码来正确处置计时器,就像您应该做的那样:

Private Sub OnDisposed(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Disposed
    If myTimer IsNot Nothing Then
        myTimer.Dispose()
        myTimer = Nothing
    End If
End Sub

是的,那会偶尔发生一次。Elapsed 事件由线程池线程引发,并且可以在任何奇数时间开始运行。如果线程池特别忙,可能需要几秒钟。处理计时器不会阻止事件运行,tp 线程已经在运行。因此,使用此特定代码,您很可能会获得 NRE。只是有时,永远不会在您调试代码时。

以受控方式停止 System.Timers.Timer 非常困难,您永远无法确定在禁用后 Elapsed 事件不会触发。防御性地写作,并记住这是可能的。并支持 System.Threading.Timer。

于 2012-04-04T17:32:38.187 回答
1

首先,您看到这些随机异常的原因:

ASystem.Timers.Timer用作System.Threading.Timer,并且正如 Hans Passant 所说,每次迭代都是在另一个线程上完成的。这使得您的Elapsed事件可以在禁用计时器后引发,因为可以在前一个迭代完成之前开始新的迭代。

工作方式System.Timers.Timer,禁用它会处理底层System.Threading.Timer并将其设置为 Nothing。在罕见的(这个词是多余的吗?)竞争条件下,您的计时器将尝试Dispose在其设置为 Nothing 时在其底层计时器上运行,从而导致NullReferenceException.


一种解决方案是将Timer.AutoReset属性设置为 False 并在您的Elapsed事件中重新启动计时器。

于 2012-04-04T21:45:15.410 回答