C# 中的 System.Threading.Timer 似乎不起作用。它每 3 秒运行得非常快

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12796148/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 00:30:04  来源:igfitidea点击:

System.Threading.Timer in C# it seems to be not working. It runs very fast every 3 second

c#.nettimer

提问by Alan Coromano

I've a timer object. I want it to be run every minute. Specifically, it should run a OnCallBackmethod and gets inactive while a OnCallBackmethod is running. Once a OnCallBackmethod finishes, it (a OnCallBack) restarts a timer.

我有一个计时器对象。我希望它每分钟运行一次。具体来说,它应该运行一个OnCallBack方法并在OnCallBack方法运行时变为非活动状态。一旦一个OnCallBack方法完成,它(一个OnCallBack)重新启动的定时器。

Here is what I have right now:

这是我现在所拥有的:

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
}

However, it seems to be not working. It runs very fast every 3 second. Even when if raise a period (1000*10). It seems like it turns a blind eye to 1000 * 10

但是,它似乎不起作用。它每 3 秒运行得非常快。即使提高一个时期(1000 * 10)。似乎对它视而不见1000 * 10

What did I do wrong?

我做错了什么?

采纳答案by Ivan Zlatanov

This is not the correct usage of the System.Threading.Timer. When you instantiate the Timer, you should almost always do the following:

这不是 System.Threading.Timer 的正确用法。当您实例化 Timer 时,您几乎应该始终执行以下操作:

_timer = new Timer( Callback, null, TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );

This will instruct the timer to tick only once when the interval has elapsed. Then in your Callback function you Change the timer once the work has completed, not before. Example:

这将指示计时器仅在间隔结束时滴答一次。然后在您的回调函数中,您在工作完成后更改计时器,而不是之前。例子:

private void Callback( Object state )
{
    // Long running operation
   _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite );
}

Thus there is no need for locking mechanisms because there is no concurrency. The timer will fire the next callback after the next interval has elapsed + the time of the long running operation.

因此不需要锁定机制,因为没有并发。计时器将在下一个时间间隔过去 + 长时间运行操作的时间后触发下一个回调。

If you need to run your timer at exactly N milliseconds, then I suggest you measure the time of the long running operation using Stopwatch and then call the Change method appropriately:

如果您需要精确地以 N 毫秒运行计时器,那么我建议您使用 Stopwatch 测量长时间运行操作的时间,然后适当地调用 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 );
}


I stronglyencourage anyone doing .NET and is using the CLR who hasn't read Jeffrey Richter's book - CLR via C#, to read is as soon as possible. Timers and thread pools are explained in great details there.

强烈鼓励任何使用 .NET 并使用 CLR 的人如果没有阅读过 Jeffrey Richter 的书 - CLR via C#,尽快阅读。那里详细解释了定时器和线程池。

回答by Ivan Leonenko

It is not necessary to stop timer, see nice solution from this post:

没有必要停止计时器,从这篇文章中看到很好的解决方案

"You could let the timer continue firing the callback method but wrap your non-reentrant code in a Monitor.TryEnter/Exit. No need to stop/restart the timer in that case; overlapping calls will not acquire the lock and return immediately."

“您可以让计时器继续触发回调方法,但将您的不可重入代码包装在 Monitor.TryEnter/Exit 中。在这种情况下无需停止/重新启动计时器;重叠调用不会获取锁定并立即返回。”

private void CreatorLoop(object state) 
 {
   if (Monitor.TryEnter(lockObject))
   {
     try
     {
       // Work here
     }
     finally
     {
       Monitor.Exit(lockObject);
     }
   }
 }

回答by Damien_The_Unbeliever

I would just do:

我只会做:

private static Timer timer;
 private static void Main()
 {
   timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
   Console.ReadLine();
 }

  private static void OnCallBack()
  {
    timer.Dispose();
    Thread.Sleep(3000); //doing some long operation
    timer = new Timer(_ => OnCallBack(), null, 1000 * 10,Timeout.Infinite); //in 10 seconds
  }

And ignore the period parameter, since you're attempting to control the periodicy yourself.

并忽略 period 参数,因为您正在尝试自己控制周期。



Your original code is running as fast as possible, since you keep specifying 0for the dueTimeparameter. From Timer.Change:

您的原始代码是尽可能快地运行,因为你把指定0dueTime参数。来自Timer.Change

If dueTime is zero (0), the callback method is invoked immediately.

如果 DueTime 为零 (0),则立即调用回调方法。

回答by Marco Mp

Is using System.Threading.Timermandatory?

使用是System.Threading.Timer强制性的吗?

If not, System.Timers.Timerhas handy Start()and Stop()methods (and an AutoResetproperty you can set to false, so that the Stop()is not needed and you simply call Start()after executing).

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

回答by Umka

 var span = TimeSpan.FromMinutes(2);
 var t = Task.Factory.StartNew(async delegate / () =>
   {
        this.SomeAsync();
        await Task.Delay(span, source.Token);
  }, source.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

source.Cancel(true/or not);

// or use ThreadPool(whit defaul options thread) like this
Task.Start(()=>{...}), source.Token)

if u like use some loop thread inside ...

如果你喜欢在里面使用一些循环线程......

public async void RunForestRun(CancellationToken token)
{
  var t = await Task.Factory.StartNew(async delegate
   {
       while (true)
       {
           await Task.Delay(TimeSpan.FromSeconds(1), token)
                 .ContinueWith(task => { Console.WriteLine("End delay"); });
           this.PrintConsole(1);
        }
    }, token) // drop thread options to default values;
}

// And somewhere there
source.Cancel();
//or
token.ThrowIfCancellationRequested(); // try/ catch block requred.