C#中的System.Threading.Timer似乎无法正常工作.它每3秒运行一次非常快

Ala*_*ano 109 .net c# timer

我有一个计时器对象.我想让它每分钟都运行一次.具体来说,它应该运行一个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,尽快阅读.定时器和线程池在那里详细解释.

  • 不会再次调用回调,这就是重点.这就是我们将Timeout.Infinite作为第二个参数传递的原因.这基本上意味着不要再次为计时器打勾.然后在我们完成操作后重新安排勾选. (30认同)
  • 我不同意`private void Callback(Object state){//长时间运行_timer.Change(TIME_INTERVAL_IN_MILLISECONDS,Tim​​eout.Infinite); }`.在操作完成之前,可能会再次调用`Callback`. (5认同)
  • 我的意思是“长时间运行的操作”可能比“TIME_INTERVAL_IN_MILLISECONDS”花费更多的时间。那会发生什么? (2认同)
  • System.Threading.Timer是一个线程池计时器,它在线程池上执行它的回调,而不是专用线程.在计时器完成回调例程之后,执行回调的线程将返回池中. (2认同)

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)


Mar*_* Mp 8

是否强制使用System.Threading.Timer?

如果没有,System.Timers.Timer有方便的Start()和Stop()方法(以及一个AutoReset属性,你可以设置为false,这样就不需要Stop(),只需在执行后调用Start()).

  • 是的,但这可能是一个真正的要求,或者只是选择了计时器,因为它是最常用的计时器.可悲的是,.NET有大量的计时器对象,重叠90%,但仍然(有时是微妙的)不同.当然,如果这是一项要求,该解决方案根本不适用. (3认同)
  • 根据[文档](https://msdn.microsoft.com/zh-cn/library/system.timers.timer(v = vs.110).aspx):Systems.Timer类在.NET中可用仅框架。它不包含在.NET标准库中,并且在其他平台(如**。NET Core或通用Windows平台)上不可用。在这些平台上,以及跨所有.NET平台的可移植性,应改用System.Threading.Timer类。** (2认同)