C# 定时器和IDisposable-在Dispose中提供额外保护?

C# 定时器和IDisposable-在Dispose中提供额外保护?,c#,.net,silverlight,timer,idisposable,C#,.net,Silverlight,Timer,Idisposable,我有一个类,它创建并使用System.Threading.Timer,如下所示: 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 = n

我有一个类,它创建并使用System.Threading.Timer,如下所示:

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;
  }
}
列表中是否有任何项目是次要的。我真正感兴趣的只是找出处理潜在启用的计时器和Dispose的最佳实践

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

编辑:

我找到了一个很好的解决方案。一个答案是,建议使用Monitor.TryEnter/Monitor.Exit保护OnTimer事件,从而有效地将OnTimer代码置于关键部分

发布了一个似乎更好的解决方案,至少在我的情况下,通过将到期时间设置为所需的间隔,并将时段设置为Timeout.Infinite,使用一次性计时器

对于我的工作,我只想在列表中至少添加了一项时启动计时器。因此,首先,我的计时器被禁用。输入Add后,禁用计时器,使其不会在Add期间触发。添加项目时,处理列表(如有必要)。在离开Add之前,如果列表中有任何项目(即,如果列表尚未处理),请启用计时器,将到期时间设置为剩余间隔,并将时段设置为Timeout.Infinite

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

编辑-我认为这是最终版本。它使用一次性计时器。当列表从零个成员变为一个成员时,计时器启用。如果我们在Add中达到计数阈值,则会处理项目并禁用计时器。对项目列表的访问由锁进行保护。对于一次性计时器是否比“正常”计时器更能为我带来好处,我持怀疑态度,除了它只会在列表中有项目并且在添加第一个项目后仅1秒时触发之外。如果我使用一个普通的计时器,那么这个序列可能会发生:快速连续添加11项。添加第11个会导致处理项目并将其从列表中删除。假设计时器还剩0.5秒。再添加1个项目。时间将在大约0.5秒后触发,一个项目将被处理并移除。使用一次触发计时器,当添加1个项目时,计时器将重新启用,并且在整个1秒间隔(或多或少)结束之前不会触发。这有关系吗?可能不会。无论如何,这里有一个版本,我认为是合理的安全,并做我想做的

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;
    }
  }
}
使用系统线程;
公共类MyClass:IDisposable
{
私有列表ints=新列表();
私人定时器t;
公共MyClass()
{
//创建处于禁用状态的计时器
t=新计时器(this.OnTimer,null,Timeout.Infinite,Timeout.Infinite);
}
私有void DisableTimer()
{
如果(t==null)返回;
t、 更改(Timeout.Infinite,Timeout.Infinite);
}
私有void EnableTimer()
{ 
如果(t==null)返回;
//1秒内发生火灾事件,但此后无事件发生。
启用计时器(1000,超时。无限);
}
私有void dosomething with theints()
{
foreach(整数中的整数i)
{
无论如何(i);
}
}
私有void OnTimer(对象状态)
{
锁(ints)
{
如果(处置)返回;
DoSomethingWithTheInts();
int.Clear();
}
}
公共空白添加(int i)
{
锁(ints)
{
如果(处置)返回;
内加(i);
如果(整数计数>10)
{
DoSomethingWithTheInts();
int.Clear();
}
如果(ints.Count==0)
{
禁用计时器();
}
其他的
如果(ints.Count==1)
{
启用计时器();
}
}
}
布尔=假;
公共空间处置()
{
如果(处置)返回;
处置(真实);
}
受保护的虚拟void Dispose(bool disposing)
{
锁(ints)
{
禁用计时器();
如果(处置)返回;
如果(t==null)返回;
t、 处置();
这是真的;
}
}
}

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

我强烈建议使用.net的并行扩展,或者(在使用.net 3.5或更早版本时)使用ReaderWriterlock类,甚至简单的Lock关键字。 还请记住System.Threading.Timer是一个异步调用,因此OnTimer是从单独的线程(从.net线程池)调用的,因此您肯定需要一些同步(比如可能锁定集合)

我想看看.NET4中的并发集合,或者在.NET3.5或更早版本中使用一些同步原语。不要在ADD方法中禁用计时器。像在OnTimer中一样编写正确的代码

lock(daObject)
{
    if (list.Count > 10)
        DoSTHWithList;
}
这段代码是最简单的(尽管肯定不是最优的)应该可以工作的代码。此外,应该将类似的代码添加到Add方法(锁定集合)。 希望它能帮助我,如果不是味精。
luke

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

如果您不针对已知的争用条件进行保护,它最终会回来咬您。谢谢。当我简化我的实际代码以便发布时,我省略了锁。只是好奇,为什么我不在OnTimer期间禁用计时器?时间间隔应该是这样的,在OnTimer期间计时器无论如何都不会启动,但是如果时间间隔很短,OnTimer需要很长时间,OnTimer可能会启动,那么我会在哪里?我想另一种方法是在OnTimer中设置一个标志,它将
lock(daObject)
{
    if (list.Count > 10)
        DoSTHWithList;
}