代码之家  ›  专栏  ›  技术社区  ›  wageoghe

定时器和IDisposable-额外的保护在处置?

  •  0
  • wageoghe  · 技术社区  · 14 年前

    using System.Threading;
    
    public class MyClass : IDisposable
    {
      private List<int> ints = new List<int>();
    
      private Timer t;
    
      public MyClass()
      {
        //Create timer in disabled state
    
        t = new Timer(this.OnTimer, null, Timeout.Infinite, Timeout.Infinite);
      }
    
      private void DisableTimer()
      {
        if (t == null) return;
    
        t.Change(Timeout.Infinite, Timeout.Infinite);
      }
    
      private void EnableTimer()
      { 
        if (t == null) return;
    
        //Fire timer in 1 second and every second thereafter.
        EnableTimer(1000, 1000);
      }
    
      private void EnableTimer(long remainingTime)
      {
        if (t == null) return;
    
        t.Change(remainingTime, 1000);
      }
    
      private void OnTimer(object state)
      {
        lock (ints) //Added since original post
        {
          DisableTimer();
    
          DoSomethingWithTheInts();
          ints.Clear();
    
          //
          //Don't reenable the timer here since ints is empty.  No need for timer
          //to fire until there is at least one element in the list.
          //
        }
      }
    
      public void Add(int i)
      {
        lock(ints) //Added since original post
        {
          DisableTimer();
    
          ints.Add(i);
          if (ints.Count > 10)
          {
            DoSomethingWithTheInts();
            ints.Clear();
          }
    
          if (ints.Count > 0)
          {
            EnableTimer(FigureOutHowMuchTimeIsLeft());
          }
        }
      }
    
      bool disposed = false;
    
      public void Dispose()
      {
        //Should I protect myself from the timer firing here?
    
        Dispose(true);
      }
    
      protected virtual void Dispose(bool disposing)
      {
        //Should I protect myself from the timer firing here?
    
        if (disposed) return;
        if (t == null) return;
    
        t.Dispose();
        disposed = true;
      }
    }
    

    编辑-在我的实际代码中,我在Add和OnTimer的列表中都有锁。当我简化代码以便发布时,我不小心遗漏了它们。

    由于计时器是一次性的,所以我在类上实现了一次性模式。我的问题是:我是否需要额外的保护在任何处置方法,以防止任何副作用的计时器事件触发?

    我可以很容易地在两种方法中禁用计时器。我知道这不一定会使我100%安全,因为在调用Dispose和禁用计时器之间计时器可能会触发。暂时撇开这个问题不谈,尽我所能保护Dispose方法不受计时器执行的可能性的影响,这是否是一种最佳做法?

    现在我考虑了一下,如果列表在Dispose中不是空的,我可能还应该考虑应该怎么做。在对象的使用模式中 应该 那时候就空着吧,但我想一切都有可能。假设在调用Dispose时,列表中还剩下一些项,继续尝试处理它们是好是坏?我想我可以说一句老话:

    public void Dispose()
    {
      if (ints != null && ints.Count > 0)
      {
        //Should never get here.  Famous last words!
      }
    }
    

    无论列表中是否有任何项都是次要的。我真正感兴趣的是找出处理可能启用的计时器和Dispose的最佳实践。

    如果重要的话,这段代码实际上在Silverlight类库中。它根本不与UI交互。

    编辑:

    我找到了一个很好的解决方案 here jsw 建议使用OnTrime.TyyTurn/MealOr.EXIT保护OnTimeEnter事件,有效地将OnTime代码放入关键部分。

    Michael Burr 发布了一个似乎更好的解决方案,至少对我来说是这样,通过将到期时间设置为所需的间隔并将周期设置为Timeout.Infinite来使用一次性计时器。

    对于我的工作,我只希望计时器在列表中至少添加了一项时触发。所以,首先,我的计时器被禁用了。输入Add时,请禁用计时器,使其在添加过程中不会触发。添加项时,处理列表(如果需要)。在离开Add之前,如果列表中有任何项(即如果列表尚未处理),请启用计时器,并将due time设置为 剩余间隔 时间段设置为Timeout.Infinite。

    对于一次性计时器,甚至不需要在OnTimer事件中禁用计时器,因为计时器无论如何都不会再次触发。当我离开OnTimer时,我也不必启用计时器,因为列表中不会有任何项目。在再次启用一次性计时器之前,我将等待另一个项目添加到列表中。 谢谢!

    using System.Threading;
    
    public class MyClass : IDisposable
    {
      private List<int> ints = new List<int>();
    
      private Timer t;
    
      public MyClass()
      {
        //Create timer in disabled state
    
        t = new Timer(this.OnTimer, null, Timeout.Infinite, Timeout.Infinite);
      }
    
      private void DisableTimer()
      {
        if (t == null) return;
    
        t.Change(Timeout.Infinite, Timeout.Infinite);
      }
    
      private void EnableTimer()
      { 
        if (t == null) return;
    
        //Fire event in 1 second but no events thereafter.
        EnableTimer(1000, Timeout.Infinite);
      }
    
      private void DoSomethingWithTheInts()
      {
        foreach (int i in ints)
        {
          Whatever(i);
        }
      }
    
      private void OnTimer(object state)
      {
        lock (ints)
        {
          if (disposed) return;
          DoSomethingWithTheInts();
          ints.Clear();
        }
      }
    
      public void Add(int i)
      {
        lock(ints)
        {
          if (disposed) return;
    
          ints.Add(i);
          if (ints.Count > 10)
          {
            DoSomethingWithTheInts();
            ints.Clear();
          }
    
          if (ints.Count == 0)
          {
            DisableTimer();
          }
          else
          if (ints.Count == 1)
          {
            EnableTimer();
          }
        }
      }
    
      bool disposed = false;
    
      public void Dispose()
      {
        if (disposed) return;
    
        Dispose(true);
      }
    
      protected virtual void Dispose(bool disposing)
      {
        lock(ints)
        {
          DisableTimer();
          if (disposed) return;
          if (t == null) return;
    
          t.Dispose();
          disposed = true;
        }
      }
    }
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   luckyluke    14 年前

    我在这段代码中看到了一些严重的同步问题。我认为你要解决的是一个经典的读者-作家问题。使用当前的方法,很可能会遇到一些问题,例如,如果有人在处理列表时试图修改它,会怎么样?

    还请记住Stult.thordy.TimeNet是异步调用,所以OnTime:从单独的线程(从.NET线程池)调用,所以您确实需要一些同步(比如可能锁定集合)。

    lock(daObject)
    {
        if (list.Count > 10)
            DoSTHWithList;
    }
    

    这段代码是最简单的(虽然肯定不是最优的),应该可以工作。还应该将类似的代码添加到Add方法(锁定集合)。 希望它有帮助,如果不是味精我。

        2
  •  1
  •   dexter    14 年前

    您不应该在OnTimer期间禁用计时器,因为您在调用周围有一个锁,因此所有线程都在等待第一个线程完成。。。