定时器线程是否等待,直到回调函数中的所有步骤都完成,或者回调函数是否在每个周期中重新调用

我有一个计时器线程function

SampleTimer = new Timer(SomeTask,0,70000) 

回拨function如下

 void SomeTask(object o) { //block using autoresetevent } 

问题是SomeTask()回调方法每70秒调用一次,即使回调方法中的所有操作仍未完成。 如何防止计时器在其中的所有步骤完成之前调用SomeTask()函数

我假设您正在使用System.Threading.Timer

一种方法是将计时器创建为一次性,然后在线程完成其任务后重新启动它。 这样你就可以确定你不会有任何重叠:

 myTimer = new Timer(someMethod, null, 70000, Timeout.Infinite); 

在你的回调中:

 void TimerCallback(object o) { // do stuff here // then change the timer myTimer.Change(70000, Timeout.Infinite); } 

指定Timeout.Infinite周期禁用周期性信号,将定时器转为一次性。

另一种方法是使用监视器:

 object TimerLock = new object(); void TimerCallback(object o) { if (!Monitor.TryEnter(TimerLock)) { // already in timer. Exit. return; } // do stuff // then release the lock Monitor.Exit(TimerLock); } 

如果你想知道为什么我不使用try/finally来锁定,请参阅Eric Lippert的博客, Locks和例外不要混合 。

这两种方法的主要区别在于,在前一次回调执行完成后,计时器将在70秒内触发。 在第二个定时器将在70秒的周期内启动,因此下一个回调可能会在前一个回复完成后的任何时间执行,从一秒后再到70秒。

对于我做过的大多数事情,我展示的第一种技术似乎效果更好。

并发调用是可能的,这是.NET和Windows计时器非常烦人的属性。 你必须自己确保相互排斥,可能只是使用一个锁(没有必要的事件)。 这给你带来了另一个问题,因为如果计时器滴答太快,任意数量的线程可能会在锁前面排队。 因此,最好尝试输入锁定,如果无法输入则不执行任何操作。 你以这种方式丢了一个勾,但是因为你的申请必须能够解决这个问题而无法保证。

与定时器一起工作时,很少能保证滴答的时间,计数和并发性。

我相信object o 被解雇的计时器。 所以你可以这样做:

 var myTimer = object as Timer; 

(但我会检查……)

无论你的SomeTask需要多长时间,它都会触发你的间隔。

我总是这样做

 private System.Timers.Timer _timer; public static void main() { _timer = SampleTimer = new Timer(SomeTask,0,70000); } void SomeTask(object o) { _timer.Enabled = false; try{ // ... work... }finally{ _timer.Enabled = true; } } 

更新:锁定更安全一点。

 private static object _padLock = new object(); void SomeTask(object o) { // lock around disabling the timer lock(_padLock) { // return if some other thread beat us to it. if (!_timer.Enabled) return; _timer.Enabled = false; } try{ // ... work... }finally{ _timer.Enabled = true; } } 

要超级安全。 如果你只想要一个“SomeTask”实例运行。 并且,您不希望它们排队。 你应该在SomeTask中放一个lock()。 您可以跳过禁用计时器。