0

我有一段代码会被相对频繁地调用。在它被调用之前,我需要一个 2000 毫秒的延迟。

想到的第一件事是每次调用该方法时创建/处理一个计时器。

为此,我使用了一个计时器(参见代码)。我的问题是......在下面的匿名方法中调用 Dispose 有任何危险/问题吗?更好的方法的建议?

执行以下操作有什么缺点吗?馊主意?

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

4 回答 4

1

非常奇怪的任务,但还可以。

如果你真的需要这样做,你真的想使用一个计时器,并且那个代码块真的会被非常频繁地调用,而不是你应该考虑使用一个计时器缓存。

每次调用代码块时,您应该检查缓存是否包含空闲计时器。如果是,则仅使用第一个可用的,否则,创建一个新的并将其放入缓存中。此外,您将需要另一个计时器,该计时器将一直工作,并会定期检查缓存中未使用的计时器。如果某些计时器或计时器在 10 秒内未使用,则将其丢弃并从缓存中删除。这种方法将显着减少创建的计时器实例的数量。

于 2012-10-17T02:42:06.997 回答
1

仅当您非常频繁地创建计时器(每秒数百次)时才需要缓存,因为多次创建和处置诸如 Timer 之类的重对象(它使用一些系统资源)可能会导致性能问题。但是从您的详细描述来看,很明显您不会经常创建计时器,因此您可以保留您的解决方案。但是您可以使用RX 的 Interval可观察集合而不是使用计时器,并且您的所有代码都将缩短为 1 个字符串:

Observable.Interval(TimeSpan.FromSeconds(2)).Take(1).Subscribe(_ => MessageBox.Show("Do something after 2000ms"));
于 2012-10-17T03:58:49.843 回答
1

您可以做的不同的一件事是将计时器的 Dispose 移到匿名方法的前面。这样,即使您稍后在方法中抛出异常,您仍然已 Disposed 计时器。我以前使用过这种模式,这是一种获得延迟回调的相当干净的方法。

如果您使用 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;
    }
于 2012-10-17T22:45:56.417 回答
0

使用您的代码创建一千个计时器时,我没有任何严重的性能问题。

在匿名方法中处理 a Timer(或 any )完全没有问题。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"));

简单得多。

于 2012-10-17T23:41:25.383 回答