我有一个计时器线程函数
SampleTimer = new Timer(SomeTask,0,70000)
回拨功能如下
void SomeTask(object o)
{
//block using autoresetevent
}
问题是SomeTask()回调方法每70秒调用一次,即使回调方法中的所有操作仍未完成。 如何防止计时器在其中的所有步骤完成之前调用SomeTask()函数
答案 0 :(得分:7)
我假设您使用的是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 and exceptions do not mix。
这两种方法的主要区别在于,在前一次回调执行完成后,计时器将在70秒后触发。在秒钟中,计时器将在70秒的时间段内启动,因此下一个回调可能会在前一个回复完成后的任何时间执行,从一秒后再到70秒后。
对于我所做的大多数事情,我展示的第一种技术似乎效果更好。
答案 1 :(得分:0)
并发调用是可能的,这是.NET和Windows计时器非常烦人的属性。你必须自己确保相互排斥,可能只是使用锁(没有必要的事件)。这给你带来了另一个问题,因为如果计时器滴答太快,任意数量的线程可能会在锁前排队。因此,最好TryEnter
锁定,如果无法输入则不执行任何操作。你以这种方式丢了一个勾号但是因为不能保证你的申请必须能够应付这个。
与定时器一起工作时,很少能保证滴答声的时间,计数和并发性。
答案 2 :(得分:0)
我相信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()。您可以跳过禁用计时器。