根据[http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx][1],您需要保留对System.Threading.Timer的引用,以防止它被丢弃。
我有一个这样的方法:
private void Delay(Action action, Int32 ms)
{
if (ms <= 0)
{
action();
}
System.Threading.Timer timer = new System.Threading.Timer(
(o) => action(),
null,
ms,
System.Threading.Timeout.Infinite);
}
我认为没有提到计时器,到目前为止我没有看到任何问题,但这可能是因为使用的延迟时间非常小。
上面的代码是错误的吗?如果是,如何保持对Timer的引用?我认为这样的事情可能有用:
class timerstate
{
internal volatile System.Threading.Timer Timer;
};
private void Delay2(Action action, Int32 ms)
{
if (ms <= 0)
{
action();
}
timerstate state = new timerstate();
lock (state)
{
state.Timer = new System.Threading.Timer(
(o) =>
{
lock (o)
{
action();
((timerstate)o).Timer.Dispose();
}
},
state,
ms,
System.Threading.Timeout.Infinite);
}
锁定业务是这样的,我可以在调用委托之前将计时器放入timerstate类。这一切对我来说都很笨拙。也许我应该考虑计时器在完成构建之前触发的可能性,并将timerstace实例中的属性分配为可忽略不计,并将锁定保留。
答案 0 :(得分:1)
更普遍地考虑你的问题,我认为你实际上想要实现的目标是以更简单的方式实现,而根本不使用System.Threading.Timer
。
这基本上是你想要的方法吗?在指定的毫秒数后执行action
?如果是这样,我会提出类似以下替代实现的建议:
private void Delay(Action action, int ms)
{
if (ms <= 0)
{
action();
return;
}
System.Threading.WaitCallback delayed = state =>
{
System.Threading.Thread.Sleep(ms);
action();
};
System.Threading.ThreadPool.QueueUserWorkItem(delayed);
}
...顺便说一句,您是否知道在您发布的代码中,为ms
指定非零值会导致action
执行两次?
timerstate
课程确实没有必要。只需将System.Threading.Timer
成员添加到包含Delay
方法的任何类中;那么你的代码应该是这样的:
public class Delayer
{
private System.Threading.Timer _timer;
private void Delay(Action action, Int32 ms)
{
if (ms <= 0)
{
action();
}
_timer = new System.Threading.Timer(
(o) => action(),
null,
ms,
System.Threading.Timeout.Infinite);
}
}
现在,我看到您将计时器的构造函数的period
参数指定为System.Threading.Timeout.Infinite
( - 1)。这意味着您希望计时器在action
结束后拨打ms
一次;我对吗?如果是这种情况,那么实际上并不需要担心计时器被处理(即,它会是,并且没关系),假设ms
的值相对较低。
无论如何,如果您要保留IDisposable
对象的实例(例如System.Threading.Timer
),则通常应该在 对象时处置该成员(即,这个实例)被处置掉。我相信System.Threading.Timer
有一个终结器,最终会导致它被处理掉,但最好在你不再需要它时立即处理它们。所以:
public class Delayer : IDisposable
{
// same code as above, plus...
public void Dispose()
{
_timer.Dispose();
}
}
答案 1 :(得分:1)
你的第二种方法也不会保留参考。在Delay2-block结束之后,对state
的引用消失了,垃圾收集器将收集它...然后你对Timer
的引用也消失了,它将被收集和处理。
class MyClass
{
private System.Threading.Timer timer;
private void Delay(Action action, Int32 ms)
{
if (ms <= 0)
{
action();
}
timer = new System.Threading.Timer(
(o) => action(),
null,
ms,
System.Threading.Timeout.Infinite);
}
}
答案 2 :(得分:1)
我从您的评论中读到了现有的答案,你可以拥有0..n动作,所以你也会有0..n计时器。是对的吗?在这种情况下,您应该执行以下操作之一:
答案 3 :(得分:1)
“工作”代码确实是不确定性垃圾回收/终结器的副作用。
此代码作为C#语句在LINQ Pad中运行,显示了此问题-由于计时器已被GC处理(并且调用了终结器并清除了内部计时器,因此将记录否消息)资源..
new System.Threading.Timer((o) => { "Hi".Dump(); }, this, 100, 100);
GC.Collect();
Thread.Sleep(2000);
但是,请注释掉“ GC.Collect”语句,并且消息将记录2秒钟,因为在程序结束之前没有[立即]执行垃圾回收器 finalizer ,因此未立即调用垃圾回收。
由于行为是不确定的,因此也应将其视为依赖于错误的错误:}
后续代码中存在相同的问题,因为需要强引用来确保对象未进行GC处理-在该示例中,仍然没有对{{1 }}包装器对象,因此存在相同的问题,尽管存在一个更高级别的间接访问。