我有一个计时器对象.我想让它每分钟都运行一次.具体来说,它应该运行一个OnCallBack
方法,并在OnCallBack
方法运行时变为非活动状态.一旦OnCallBack
方法完成,它(a OnCallBack
)重新启动计时器.
这就是我现在所拥有的:
private static Timer timer;
private static void Main()
{
timer = new Timer(_ => OnCallBack(), null, 0, 1000 * 10); //every 10 seconds
Console.ReadLine();
}
private static void OnCallBack()
{
timer.Change(Timeout.Infinite, Timeout.Infinite); //stops the timer
Thread.Sleep(3000); //doing some long operation
timer.Change(0, 1000 * 10); //restarts the timer
}
Run Code Online (Sandbox Code Playgroud)
但是,似乎没有用.它每3秒运行一次非常快.即使提高了一段时间(1000*10).它似乎对此视而不见1000 * 10
我做错了什么?
Iva*_*nov 222
这不是System.Threading.Timer的正确用法.实例化Timer时,几乎总是应该执行以下操作:
_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
Run Code Online (Sandbox Code Playgroud)
这将指示计时器在间隔过去时仅勾选一次.然后在您的回调功能中,您可以在工作完成后更改计时器,而不是之前.例:
private void Callback( Object state )
{
// Long running operation
_timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}
Run Code Online (Sandbox Code Playgroud)
因此,不需要锁定机制,因为没有并发性.定时器将在下一个间隔过去+长时间运行操作的时间后触发下一个回调.
如果你需要在N毫秒内运行计时器,那么我建议你使用秒表测量长时间运行的时间,然后适当地调用Change方法:
private void Callback( Object state )
{
Stopwatch watch = new Stopwatch();
watch.Start();
// Long running operation
_timer.Change( Math.Max( 0, TIME_INTERVAL_IN_MILLISECONDS - watch.ElapsedMilliseconds ), Timeout.Infinite );
}
Run Code Online (Sandbox Code Playgroud)
我强烈建议任何人在使用.NET并使用没有阅读过Jeffrey Richter的书的CLR - 通过C#的CLR,尽快阅读.定时器和线程池在那里详细解释.
Iva*_*nko 14
没有必要停止计时器,从这篇文章看到很好的解决方案:
"您可以让计时器继续触发回调方法,但将不可重入的代码包装在Monitor.TryEnter/Exit中.在这种情况下,不需要停止/重启计时器;重叠调用将不会获取锁定并立即返回."
private void CreatorLoop(object state)
{
if (Monitor.TryEnter(lockObject))
{
try
{
// Work here
}
finally
{
Monitor.Exit(lockObject);
}
}
}
Run Code Online (Sandbox Code Playgroud)
是否强制使用System.Threading.Timer?
如果没有,System.Timers.Timer有方便的Start()和Stop()方法(以及一个AutoReset属性,你可以设置为false,这样就不需要Stop(),只需在执行后调用Start()).