使用Timer处理类的正确方法是什么?

时间:2015-05-13 20:51:57

标签: c# .net vb.net timer

假设我有一个具有Timer对象的类,它不执行任何关键工作 - 只是一些GUI工作。假设有两种情况,计时器每5分钟流逝一次:

  1. Timer_Elapsed代表中有很多工作要完成,需要2分钟才能完成。
  2. Timer_Elapsed代表中,没有什么工作要做,完成需要几毫秒
  3. 处理物体的正确方法是什么?计时器? Timer_Elapsed事件委托的运行时间是否会影响您对如何正确处置的决定?

2 个答案:

答案 0 :(得分:5)

如果,您需要在处理过程中停止计时器,并且计时器委托中的工作仍然可以进行,这依赖于共享资源,同时处理,您需要协调“关闭”过程。下面的代码段显示了这样做的一个示例:

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

答案 1 :(得分:1)

有多种方法可以解决这个问题,就像Alex在评论中所说,它取决于代表将使用的对象是否也被处理掉。

我们说我们有一个&#34;最糟糕的情况&#34;场景,其中委托确实需要使用将被处置的对象 处理此问题的好方法类似于Process对象具有的方法:WaitForExit()。这个方法只是循环,直到它看到委托完成工作(有一个working bool,它在委托运行之前和之后设置?)然后返回。现在,您可以在使用该类的代码中使用类似的内容:

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

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