在匿名方法内部配置Timer

时间:2012-10-17 01:55:33

标签: c# .net timer dispose anonymous

我有一块相对经常被调用的代码块。在被叫之前,我需要延迟2000毫秒。

首先想到的是每次调用方法时都会创建/处理一个计时器。

为了实现这一点,我正在使用Timer(参见代码)。我的问题是......在下面的匿名方法中调用Dispose的任何危险/问题?建议采用更好的方法吗?

执行以下操作是否有任何缺点?不好主意?

delayTimer = new Timer() { Interval = 2000 };
{
    delayTimer.Tick += (sender2, e2) => 
    { 
        ((Timer)sender2).Stop();     
        MessageBox.Show("Do something after 2000ms"); 
        delayTimer.Dispose(); 
    };
}

4 个答案:

答案 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"));

更简单。