2

今天在一台机器上发生的 NullReference 异常是否有任何解释。我无法在我的计算机上复制它......

class Test
{
  Timer timer_;
  public void Init()
  {
    timer_ = new Timer();
    timer_.Interval = 10000;
    timer_.Tick += OnTimerTick;
    timer_.Start();
  }

  private void OnTimerTick(object sender, EventArgs e)
  {
    timer_.Stop();
    timer_ = null; <--- Null Ref occurs
  }
}

基于 Mark Hall 和 Rich Okelly 的绝妙建议的解决方案

private void OnTimerTick(object sender, EventArgs e)
{
    var localTimer = Interlocked.Exchange(ref timer_, null);
    if (localTimer != null)
    {
        localTimer.Stop();
        localTimer.Tick -= OnTimerTick;
        localTimer.Dispose();

        // doing staff
    }
}
4

3 回答 3

3

在将计时器设置为空之前尝试删除您的 OnTimerTick 事件。这将防止在您将其设置为 null 时将其提升,但由于您正在创建一个不太可能的 10 秒单次拍摄,因此请尝试在将其设置为 null 之前处理掉您的计时器;

IE

private void OnTimerTick(object sender, EventArgs e) 
{ 
    timer_.Stop(); 
    timer_.Tick -= OnTimerTick;
    timer_.Dispose();
    timer_ = null; 
}
于 2012-07-04T15:31:37.033 回答
2

我认为空引用异常实际上发生在上面的行: at timer_.Stop()

发生的事情是引发了 Tick 事件并安排了另一个,由于第一个 Tick 事件,计时器被停止并设置为空。然后,第二个 Tick 事件尝试在 Timer 上调用 Stop,该 Timer 现在为空。

您可以使用 Interlocked 方法来解决此问题:

private void OnTimerTick(object sender, EventArgs e)
{
  var localTimer= Interlocked.Exchange(ref timer_, null);
  if (localTimer != null) 
  {
     localTimer.Stop();
  }
}
于 2012-07-04T15:29:17.473 回答
0

您说您正在使用System.Windows.Forms.Timer,文档说:

此 Windows 计时器专为使用 UI 线程执行处理的单线程环境而设计。它要求用户代码具有可用的 UI 消息泵,并且始终从同一线程操作,或将调用编组到另一个线程。

所以不需要使用 Interlocked.Exchange,这不是并发问题。

您可以尝试以下代码:

  public void Init()       
  {       
    if (timer_ != null)
      throw new InvalidOperationException("Already initialized!");
    timer_ = new Timer();       
    timer_.Interval = 10000;       
    timer_.Tick += OnTimerTick;       
    timer_.Start();       
  }       


  private void OnTimerTick(object sender, EventArgs e) 
  { 
    if (timer_ != null)
    {
      timer_.Stop();
      timer_.Dispose();
      timer_ = null;

      // Your code
    }
  } 

这样,在第一个滴答时 timer_ 将停止并设置为 null。如果有任何待处理的 Tick,由于 (timer_ != null),它将被忽略。

此外,如果在计时器运行时调用 Init()(可能是一个错误),您很快就会看到它。

于 2012-07-05T08:35:47.333 回答