我有一个计时器线程功能
SampleTimer = new Timer(SomeTask,0,70000)
回调函数如下
void SomeTask(object o)
{
//block using autoresetevent
}
问题是 SomeTask() 回调方法每 70 秒调用一次,即使回调方法中的所有操作仍未完成。如何防止计时器在其中的所有步骤完成之前调用 SomeTask() 函数
我有一个计时器线程功能
SampleTimer = new Timer(SomeTask,0,70000)
回调函数如下
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);
}
如果您想知道为什么我不使用 atry/finally
作为锁,请参阅 Eric Lippert 的博客Locks and exceptions do not mix。
这两种方法的主要区别在于,在第一种方法中,计时器将在前一个回调执行完成后 70 秒触发。在一秒钟内,计时器将在 70 秒的周期内触发,因此下一个回调可能会在前一个回调完成后的任何时间执行,从一秒后到 70 秒后。
对于我所做的大多数事情,我展示的第一个技术似乎效果更好。
并发调用是可能的,这是 .NET 和 Windows 计时器的一个非常烦人的属性。您必须自己确保互斥,也许只是使用锁(不需要事件)。但是,这给您带来了另一个问题,因为如果计时器计时太快,则任意数量的线程可能会在锁前排队。所以最好TryEnter
锁好,进不去就什么都不做。您以这种方式丢失了一个刻度,但由于无论如何都不能保证刻度,您的应用程序必须能够应对这种情况。
使用计时器时,几乎无法保证滴答的时间、计数和并发性。
我相信这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()。您可以跳过禁用计时器。