可靠地停止System.Threading.Timer?

Jay*_*Pea 53 c# multithreading timer

好吧,我已经搜索了很多解决方案.我正在寻找一种干净简单的方法来防止在我停止后调用System.Threading.Timer的回调方法.

我似乎无法找到任何东西,这导致我,在偶尔,诉诸可怕的线程 - thread.sleep-thread.abort组合颤抖.

可以用锁来完成吗?请帮我找到一个好办法.谢谢

Owe*_*ker 107

一个更简单的解决方案可能是设置Timer永不恢复; 方法Timer.Change可以获取值,dueTimeperiod指示计时器永远不会重新启动:

this.Timer.Change(Timeout.Infinite, Timeout.Infinite);
Run Code Online (Sandbox Code Playgroud)

虽然改用使用System.Timers.Timer可能是一种"更好"的解决方案,但总会有时候这种方法不实用; 只需使用Timeout.Infinite就足够了.

  • 这是最好的答案.海报和读者可能正在使用System.Threading.Timers是有充分理由的(像我一样).这个方法只是停止计时器,这正是我所需要的. (9认同)

Jal*_*aid 40

Conrad Frix建议您应该使用System.Timers.Timer该类,例如:

private System.Timers.Timer _timer = new System.Timers.Timer();
private volatile bool _requestStop = false;

public constructor()
{
    _timer.Interval = 100;
    _timer.Elapsed += OnTimerElapsed;
    _timer.AutoReset = false;
    _timer.Start();
}

private void OnTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // do work....
    if (!_requestStop)
    {
        _timer.Start();//restart the timer
    }
}

private void Stop()
{
    _requestStop = true;
    _timer.Stop();
}

private void Start()
{
    _requestStop = false;
    _timer.Start();
}
Run Code Online (Sandbox Code Playgroud)

  • 我对此答案感到困惑的原因是它似乎没有解决问题.Stop()的调用者仍然无法确定Stop()调用后是否执行"do work"部分.而且我认为这是最初的问题/问题. (4认同)

Rol*_*sen 10

对于System.Threading.Timer,可以执行以下操作(还将保护回调方法不处理已处理的计时器 - ObjectDisposedException):

class TimerHelper : IDisposable
{
    private System.Threading.Timer _timer;
    private readonly object _threadLock = new object();

    public event Action<Timer,object> TimerEvent;

    public void Start(TimeSpan timerInterval, bool triggerAtStart = false,
        object state = null)
    {
        Stop();
        _timer = new System.Threading.Timer(Timer_Elapsed, state,
            System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);

        if (triggerAtStart)
        {
            _timer.Change(TimeSpan.FromTicks(0), timerInterval);
        }
        else
        {
            _timer.Change(timerInterval, timerInterval);
        }
    }

    public void Stop(TimeSpan timeout = TimeSpan.FromMinutes(2))
    {
        // Wait for timer queue to be emptied, before we continue
        // (Timer threads should have left the callback method given)
        // - http://woowaabob.blogspot.dk/2010/05/properly-disposing-systemthreadingtimer.html
        // - http://blogs.msdn.com/b/danielvl/archive/2011/02/18/disposing-system-threading-timer.aspx
        lock (_threadLock)
        {
            if (_timer != null)
            {
                ManualResetEvent waitHandle = new ManualResetEvent(false)
                if (_timer.Dispose(waitHandle))
                {
                   // Timer has not been disposed by someone else
                   if (!waitHandle.WaitOne(timeout))
                      throw new TimeoutException("Timeout waiting for timer to stop");
                }
                waitHandle.Close();   // Only close if Dispose has completed succesful
                _timer = null;
            }
        }
    }

    public void Dispose()
    {
        Stop();
        TimerEvent = null;
    }

    void Timer_Elapsed(object state)
    {
        // Ensure that we don't have multiple timers active at the same time
        // - Also prevents ObjectDisposedException when using Timer-object
        //   inside this method
        // - Maybe consider to use _timer.Change(interval, Timeout.Infinite)
        //   (AutoReset = false)
        if (Monitor.TryEnter(_threadLock))
        {
            try
            {
                if (_timer==null)
                    return;

                Action<Timer, object> timerEvent = TimerEvent;
                if (timerEvent != null)
                {
                    timerEvent(_timer, state);
                }
            }
            finally
            {
                Monitor.Exit(_threadLock);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是人们可以使用它的方式:

void StartTimer()
{
    TimerHelper _timerHelper = new TimerHelper();
    _timerHelper.TimerEvent += (timer,state) => Timer_Elapsed();
    _timerHelper.Start(TimeSpan.FromSeconds(5));
    System.Threading.Sleep(TimeSpan.FromSeconds(12));
    _timerHelper.Stop();
}

void Timer_Elapsed()
{
   // Do what you want to do
}
Run Code Online (Sandbox Code Playgroud)


Wil*_*l A 9

MSDN文档建议您使用Dispose(WaitHandle)方法停止计时器+回调时将不再被调用的通知.


500*_*ror 6

对于它的价值,我们使用这种模式:

// set up timer
Timer timer = new Timer(...);
...

// stop timer
timer.Dispose();
timer = null;
...

// timer callback
{
  if (timer != null)
  {
    ..
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是线程安全的,因为(1)可以从线程更新timer = null,并且回调线程仍然可以看到它的旧值.(2)如果在"if(timer!= null)"检查后使用定时器内部的定时器实例回调; 它可能为null,因为另一个线程调用stop方法并使其为null.我真的不推荐这种模式,除非你做一些线程安全的东西,如锁...... (9认同)