有什么可能导致System.Timers.Timer.set_Enabled属性抛出System.NullReferenceException的想法?

时间:2012-04-04 15:29:15

标签: c# .net vb.net winforms

这是堆栈跟踪:

  

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事件的事件处理程序中,但它只是在用户计算机上偶尔发生。我自己无法重现错误。

2 个答案:

答案 0 :(得分: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

是的,偶尔会去kaboom。 Elapsed事件由线程池线程引发,可以在任何奇怪的时间开始运行。如果线程池特别繁忙,则可能需要几秒钟。处置计时器阻止事件运行,tp线程已经在飞行中。因此,使用此特定代码,您很可能会获得NRE。有时候,从来没有调试代码。

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

答案 1 :(得分:1)

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

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

System.Timers.Timer的工作方式,禁用它会释放基础System.Threading.Timer并将其设置为Nothing。在一个罕见的(这个词是多余的?)竞争条件下,你的计时器将在其基础计时器上运行Dispose,同时设置为Nothing,从而产生NullReferenceException


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