可靠地停止System.Threading.Timer?

那么我已经search了很多的解决scheme。 我正在寻找一种干净而简单的方法来阻止System.Threading.Timer的callback方法在我停止之后被调用。

我似乎无法find任何东西,这引起我,在诉诸,诉诸可怕的线程thread.sleep-thread.abort组合不寒而栗。

可以用锁来完成吗? 请帮助我find一个很好的方法来做到这一点。 谢谢

像康拉德Frixbuild议你应该使用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(); } 

一个更简单的解决scheme可能是设置Timer永远不会恢复; Timer.Change方法可以取值为dueTime和指示定时器永不重新启动的period

 this.Timer.Change(Timeout.Infinite, Timeout.Infinite); 

虽然改变使用System.Timers.Timer可能是一个“更好”的解决scheme,总是会有一些时候,这是不切实际的; 只要使用Timeout.Infinite就足够了。

对于System.Threading.Timer,可以执行以下操作(还将保护callback方法不受处理计时器的影响 – 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); } } } } 

这是如何使用它的:

 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 } 

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

 // set up timer Timer timer = new Timer(...); ... // stop timer timer.Dispose(); timer = null; ... // timer callback { if (timer != null) { .. } } 

MSDN文档build议您使用Dispose(WaitHandle)方法来停止计时器+被通知不再调用callback。

对我来说,这似乎是正确的方法:只要在完成定时器时调用dispose 。 这将停止计时器,并阻止未来计划的呼叫。

看下面的例子。

 class Program { static void Main(string[] args) { WriteOneEverySecond w = new WriteOneEverySecond(); w.ScheduleInBackground(); Console.ReadKey(); w.StopTimer(); Console.ReadKey(); } } class WriteOneEverySecond { private Timer myTimer; public void StopTimer() { myTimer.Dispose(); myTimer = null; } public void ScheduleInBackground() { myTimer = new Timer(RunJob, null, 1000, 1000); } public void RunJob(object state) { Console.WriteLine("Timer Fired at: " + DateTime.Now); } } 

这个答案涉及到System.Threading.Timer

我读过很多关于如何在networking中同步处理System.Threading.Timer的废话。 所以这就是为什么我发布这个试图纠正这种情况。 随时告诉我/如果我正在写的东西是错误的打电话给我;-)

陷阱

在我看来有这些陷阱:

  • Timer.Dispose(WaitHandle)可以返回false。 它是这样做的,如果它已经被处置(我不得不看源代码)。 在这种情况下,它不会设置WaitHandle – 所以不要等待!
  • 不处理WaitHandle超时。 严重 – 如果你对超时没有兴趣,你还在等什么?
  • 并发性问题,如上所述在msdn上ObjectDisposedException可能发生处理期间 (而不是后)。
  • Timer.Dispose(WaitHandle)无法正常工作 – Slim等待手柄,或不如人们所期望的。 例如,以下不起作用(它永远阻止):
  using(var manualResetEventSlim = new ManualResetEventSlim) { timer.Dispose(manualResetEventSlim.WaitHandle); manualResetEventSlim.Wait(); } 

那么标题有点“大胆”我猜,但下面是我的尝试来处理这个问题 – 一个处理双重处置,超时和ObjectDisposedException的包装。 它不提供Timer上的所有方法,但是可以随意添加它们。

 internal class Timer { private readonly TimeSpan _disposalTimeout; private readonly System.Threading.Timer _timer; private bool _disposeEnded; public Timer(TimeSpan disposalTimeout) { _disposalTimeout = disposalTimeout; _timer = new System.Threading.Timer(HandleTimerElapsed); } public event Signal Elapsed; public void TriggerOnceIn(TimeSpan time) { try { _timer.Change(time, Timeout.InfiniteTimeSpan); } catch (ObjectDisposedException) { // race condition with Dispose can cause trigger to be called when underlying // timer is being disposed - and a change will fail in this case. // see // https://msdn.microsoft.com/en-us/library/b97tkt95(v=vs.110).aspx#Anchor_2 if (_disposeEnded) { // we still want to throw the exception in case someone really tries // to change the timer after disposal has finished // of course there's a slight race condition here where we might not // throw even though disposal is already done. // since the offending code would most likely already be "failing" // unreliably i personally can live with increasing the // "unreliable failure" time-window slightly throw; } } } private void HandleTimerElapsed(object state) { Elapsed.SafeInvoke(); } public void Dispose() { using (var waitHandle = new ManualResetEvent(false)) { // returns false on second dispose if (_timer.Dispose(waitHandle)) { if (!waitHandle.WaitOne(_disposalTimeout)) { throw new TimeoutException( "Timeout waiting for timer to stop. (...)"); } _disposeEnded = true; } } } } 

也许你应该做相反的事情。 使用system.timers.timer,将AutoReset设置为false,只在需要时启动它

你不能保证你应该停止定时器的代码将在定时器事件调用之前执行。 例如,假设在时刻0时刻,当时刻5到来时,初始化定时器来调用事件。 然后准时3你决定你不再需要这个电话。 和你想写在这里的方法。 然后,虽然方法是JIT的时间来到时刻4和OS决定你的线程耗尽其时间片和开关。 无论您如何尝试,计时器都会调用事件 – 您的代码在最坏的情况下不会有机会运行。

这就是为什么在事件处理程序中提供一些逻辑更安全的原因。 也许一些ManualResetEvent在您不再需要事件调用时将会被重置。 所以你configuration计时器,然后设置ManualResetEvent。 在定时器事件处理程序中,首先要testingManualResetEvent。 如果它处于复位状态 – 立即返回。 因此,您可以有效防范某些代码的意外执行。

你可以通过创build一个像这样的类来停止计时器,并从例如你的callback方法中调用它:

 public class InvalidWaitHandle : WaitHandle { public IntPtr Handle { get { return InvalidHandle; } set { throw new InvalidOperationException(); } } } 

实例化定时器:

 _t = new Timer(DisplayTimerCallback, TBlockTimerDisplay, 0, 1000); 

然后里面的callback方法:

 if (_secondsElapsed > 80) { _t.Dispose(new InvalidWaitHandle()); } 

有一个MSDN链接如何正确实现停止计时器。 使用ControlThreadProc()方法,通过syncPoint静态类variables同步HandleElapsed(object sender, ElapsedEventArgs e)事件。 注释掉Thread.Sleep(testRunsFor);ControlThreadProc()如果它不适合(可能)。 关键在于在条件语句中使用静态variables和像Interlocked.CompareExchange这样的primefaces操作。

链接: Timer.Stop方法