使用带有Threading.Timer的锁

我有一个Windows服务应用程序,它使用Threading.TimerTimerCallback以特定的间隔进行一些处理。 我需要一次将此处理代码锁定为仅1个线程。

因此,例如,启动服务并触发第一个回调并启动一个线程并开始处理。 只要在下一次回调之前完成处理,这就可以正常工作。 所以说比如说处理比通常花费的时间稍长,并且在另一个线程正在处理时再次触发TimerCallback,我需要让该线程等到另一个线程完成。

这是我的代码示例:

 static Timer timer; static object locker = new object(); public void Start() { var callback = new TimerCallback(DoSomething); timer = new Timer(callback, null, 0, 10000); } public void DoSomething() { lock(locker) { // my processing code } } 

这是一种安全的方法吗? 如果队列变得相当大,会发生什么? 有更好的选择吗?

如果您可以使用它们之间的恒定间隔触发事件(与以恒定间隔触发它们的当前代码相反),那么您可以在没有句点的情况下启动计时器,并且每次排队新的回调时,例如

 static Timer timer; public void Start() { var callback = new TimerCallback(DoSomething); timer = new Timer(callback, null, 0, Timeout.Infinite); } public void DoSomething() { try { // my processing code } finally { timer.Change(10000, Timeout.Infinite); } } 

此代码告诉新创建的计时器立即触发,仅一次。 在处理代码中,它完成工作,然后告诉计时器在10秒内再次触发,仅一次。 因为定时器现在不是定期触发但是通过其回调方法重新启动,所以回调保证是单线程的,没有队列。

如果你想保持一个恒定的间隔,那么它有点棘手,因为你必须决定如果处理开始花费的时间超过定时器间隔该怎么办。 一种选择是执行您当前正在执行的操作,但这实际上会导致大量排队的线程和最终的线程池饥饿。 另一种选择是,如果已经有一个回调,则简单地丢弃回调,例如

 static Timer timer; static object locker = new object(); public void Start() { var callback = new TimerCallback(DoSomething); timer = new Timer(callback, null, 0, 10000); } public void DoSomething() { if (Monitor.TryEnter(locker)) { try { // my processing code } finally { Monitor.Exit(locker); } } } 

如果处理代码执行时间超过10秒,最糟糕的情况是,每次调用一个新的回调时,你将浪费一个线程池线程(它们将在lock语句中等待)。 如果你采取所有线程池线程HttpWebRequest,ASP.NET,异步委托调用…将受到影响。

我要做的是立即安排第一次回调。 然后,如果你真的需要每隔10秒调用一次DoSomething()

 public void DoSomething () { DateTime start = DateTime.UtcNow; ... TimeSpan elapsed = (DateTime.UtcNow - start); int due_in = (int) (10000 - elapsed.TotalMilliseconds); if (due_in < 0) due_in = 0; timer.Change (due_in, Timeout.Infinite); } 

或者那条线上的东西。