我有一块相对经常被调用的代码块。在被叫之前,我需要延迟2000毫秒。
首先想到的是每次调用方法时都会创建/处理一个计时器。
为了实现这一点,我正在使用Timer(参见代码)。我的问题是......在下面的匿名方法中调用Dispose的任何危险/问题?建议采用更好的方法吗?
执行以下操作是否有任何缺点?不好主意?
delayTimer = new Timer() { Interval = 2000 };
{
delayTimer.Tick += (sender2, e2) =>
{
((Timer)sender2).Stop();
MessageBox.Show("Do something after 2000ms");
delayTimer.Dispose();
};
}
答案 0 :(得分:1)
如果您经常创建计时器(每秒数百次),则需要缓存,因为多次创建和处理如Timer(它使用某些系统资源)这样的繁重对象会导致性能问题。但是从您的详细描述中可以明显看出,您不会经常创建计时器,因此您可以保留解决方案。但是,您可以使用RX's Interval可观察集合而不是使用计时器,并且所有代码都将缩短为1个字符串:
Observable.Interval(TimeSpan.FromSeconds(2)).Take(1).Subscribe(_ => MessageBox.Show("Do something after 2000ms"));
答案 1 :(得分:1)
您可以做的不同之处是将计时器的Dispose移动到匿名方法的前面。这样,即使您稍后在方法中抛出异常,您仍然会处理计时器。我之前使用过这种模式,这是一种相当干净的方式来获得延迟回调。
如果您正在使用C#5,那么可以使用非常好的Task.Delay方法在异步方法中获取计时器回调。这通常用于实现超时以及对WaitAny的调用,如下所示:
public static async Task WithTimeout(this Task task, int timeout, string timeoutMessage = null, params object[] args)
{
var timeoutTask = Task.Delay(timeout);
if (await Task.WhenAny(task, timeoutTask) == timeoutTask)
throw new TimeoutException(timeoutMessage == null ? "Operation timed out" : string.Format(timeoutMessage, args));
await task;
}
答案 2 :(得分:0)
非常奇怪的任务,但还可以。
如果你真的需要这样做,你真的想要使用一个计时器,而且你应该经常调用那段代码,而不应该考虑使用计时器缓存。
每次调用代码块时,都应该检查缓存是否包含空闲计时器。如果是,请使用第一个可用的,否则,创建一个新的并将其放入缓存中。此外,您还需要一个可以一直工作的另一个计时器,并定期检查缓存中是否有未使用的计时器。如果某些计时器或计时器未使用,比如10秒钟,则处理它并从缓存中删除。这种方法将显着减少创建的计时器实例的数量。
答案 3 :(得分:0)
我没有任何严重的性能问题,使用您的代码创建了数千个计时器。
在匿名方法中处置Timer
(或任何IDisposable
)完全没问题。
但是,您的代码不是以尽可能好的方式编写的。请尝试此实现:
var delayTimer = new Timer()
{
Interval = 2000,
Enabled = true,
};
EventHandler tick = null;
tick = (_s, _e) =>
{
delayTimer.Tick -= tick;
delayTimer.Stop();
delayTimer.Dispose();
MessageBox.Show("Do something after 2000ms");
};
delayTimer.Tick += tick;
在尝试处理计时器之前分离事件总是一个好主意。事实上,可能有很多次无法分离将无法正常清理GC,并且您可能会发生内存泄漏。
尽管如此,我确实喜欢Rx对这个问题的回答是最干净的方法。虽然使用Rx不会对UI线程上的回调进行编组,除非你这样做:
Observable
.Timer(TimeSpan.FromSeconds(2.0))
.ObserveOn(this) // assuming `this` is your form
.Subscribe(_ => MessageBox.Show("Do something after 2000ms"));
更简单。