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