【问题标题】:What is the proper way to Dispose of a class with a Timer?使用 Timer 处理类的正确方法是什么?
【发布时间】:2015-05-14 22:18:53
【问题描述】:

假设我有一个类,它有一个不做任何关键工作的 Timer 对象——只是一些 GUI 工作。假设有两种情况,计时器每 5 分钟经过一次:

  1. Timer_Elapsed 代表中完成了很多工作,需要 2 分钟才能完成。
  2. Timer_Elapsed 委托中几乎没有什么工作要做,需要几毫秒才能完成

处理对象和计时器的正确方法是什么? Timer_Elapsed 事件委托运行的时间量是否会影响您对如何正确处理的决定?

【问题讨论】:

  • 不是它本身花费的时间,而是它做了什么。 IE。如果它使用将要同时释放的资源,您可能必须与计时器完成同步,并允许协作取消委托中正在完成的工作。
  • @Alex - 那么你将如何为这样的类编写 dispose 方法?
  • @Plutonix - 不是我要问的。你说的是给定的。我认为亚历克斯在“合作取消”方面走在了正确的轨道上,但我很好奇他会怎么做。

标签: c# .net vb.net timer


【解决方案1】:

如果您需要在处置期间停止计时器,并且您的计时器委托中的工作可能仍在进行中,这依赖于共享资源,同时被处置,您需要协调“关闭”过程。下面的 sn-p 显示了执行此操作的示例:

public class PeriodicTimerTask : IDisposable
{
    private readonly System.Timers.Timer _timer;
    private CancellationTokenSource _tokenSource;
    private readonly ManualResetEventSlim _callbackComplete;
    private readonly Action<CancellationToken> _userTask;

    public PeriodicTimerTask(TimeSpan interval, Action<CancellationToken> userTask)
    {
        _tokenSource = new CancellationTokenSource();
        _userTask = userTask;
        _callbackComplete = new ManualResetEventSlim(true);
        _timer = new System.Timers.Timer(interval.TotalMilliseconds);
    }

    public void Start()
    {
        if (_tokenSource != null)
        {
            _timer.Elapsed += (sender, e) => Tick();
            _timer.AutoReset = true;
            _timer.Start();
        }
    }

    public void Stop()
    {
        var tokenSource = Interlocked.Exchange(ref _tokenSource, null);
        if (tokenSource != null)
        {
            _timer.Stop();
            tokenSource.Cancel();
            _callbackComplete.Wait();
            _timer.Dispose();
            _callbackComplete.Dispose();
            tokenSource.Dispose();
        }
    }

    public void Dispose()
    {
        Stop();
        GC.SuppressFinalize(this);
    }

    private void Tick()
    {
        var tokenSource = _tokenSource;
        if (tokenSource != null && !tokenSource.IsCancellationRequested)
        {
            try
            {
                _callbackComplete.Wait(tokenSource.Token); // prevent multiple ticks.
                _callbackComplete.Reset();
                try
                {
                    tokenSource = _tokenSource;
                    if (tokenSource != null && !tokenSource.IsCancellationRequested)
                        _userTask(tokenSource.Token);
                }
                finally
                {
                    _callbackComplete.Set();
                }
            }
            catch (OperationCanceledException) { }
        }
    }
}

使用示例:

public static void Main(params string[] args)
{
    var periodic = new PeriodicTimerTask(TimeSpan.FromSeconds(1), cancel => {
        int n = 0;
        Console.Write("Tick ...");
        while (!cancel.IsCancellationRequested && n < 100000)
        {
            n++;
        }
        Console.WriteLine(" completed.");
    });
    periodic.Start();
    Console.WriteLine("Press <ENTER> to stop");
    Console.ReadLine();
    Console.WriteLine("Stopping");
    periodic.Dispose();
    Console.WriteLine("Stopped");
}

输出如下:

Press <ENTER> to stop
Tick ... completed.
Tick ... completed.
Tick ... completed.
Tick ... completed.
Tick ... completed.

Stopping
Stopped

【讨论】:

  • 您可以使用System.Threading.Timer 来防止tick reentrency,而无需手动重置事件。只需在每个刻度结束时使用Change(nextRunTime, Timeout.Infinite)
  • @Zer0,该事件的主要原因是需要等待用户委托完成。考虑到(我相信)OP 在使用System.Timers.Timer 时要求解决方案,重入使用纯粹是偶然的。但这是一个很好的观点,不同的计时器机制可能更可取。
  • 觉得值得一提。但是 +1 表示线程安全 Stop 并使用 CancellationTokenSource
【解决方案2】:

对此有多种方法,就像 Alex 在 cmets 中所说的,这取决于委托将使用的对象是否也被释放。

假设我们有一个“最坏情况”的场景,其中委托确实需要使用将被释放的对象。
处理此问题的好方法类似于Process 对象具有的方法:WaitForExit()。此方法将简单地循环,直到它看到委托完成工作(在委托运行之前和之后设置了一个working bool?)然后返回。现在您可以使用该类在代码中包含类似的内容:

// Time to shut down
myDisposable.WaitForFinish();
myDisposable.Dispose();

因此,我们基本上确保在处理委托之前完成委托,停止任何形式的ObjectDisposedException

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-06-02
    • 1970-01-01
    • 2010-12-06
    • 2015-02-11
    • 2014-09-23
    • 2010-10-08
    • 2013-07-24
    • 2011-12-04
    相关资源
    最近更新 更多