wake-up-neo.com

System.Threading.Timer in C # scheint nicht zu funktionieren. Es läuft alle 3 Sekunden sehr schnell

Ich habe ein Timerobjekt. Ich möchte, dass es jede Minute ausgeführt wird. Insbesondere sollte eine OnCallBack-Methode ausgeführt werden und wird deaktiviert, während eine OnCallBack-Methode ausgeführt wird. Sobald eine OnCallBack-Methode abgeschlossen ist, startet sie (eine OnCallBack) einen Timer neu.

Hier ist was ich gerade habe:

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
}

Es scheint jedoch nicht zu funktionieren. Es läuft alle 3 Sekunden sehr schnell. Auch wenn, wenn Sie eine Periode erhöhen (1000 * 10). Es scheint, als würde ein Auge für 1000 * 10

Was habe ich falsch gemacht?

102
Alan Coromano

Dies ist nicht die korrekte Verwendung des System.Threading.Timer. Wenn Sie den Timer instanziieren, sollten Sie fast immer Folgendes tun:

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

Dadurch wird der Timer angewiesen, nur einmal zu ticken, wenn das Intervall abgelaufen ist. Dann ändern Sie in Ihrer Rückruffunktion den Timer, sobald die Arbeit abgeschlossen ist, nicht vorher. Beispiel:

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

Daher sind keine Sperrmechanismen erforderlich, da keine Parallelität besteht. Der Timer löst den nächsten Rückruf aus, nachdem das nächste Intervall abgelaufen ist + die Zeit des langen Betriebs.

Wenn Sie Ihren Timer genau N Millisekunden ausführen müssen, empfiehlt es sich, die Zeit der Langzeitoperation mit Stoppuhr zu messen und die Change-Methode entsprechend aufzurufen:

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 );
}

Ich stark ermutige jeden, der .NET macht und verwendet die CLR, die das Buch von Jeffrey Richter nicht gelesen hat - CLR über C #, um so schnell wie möglich zu lesen. Zeitgeber und Thread-Pools werden dort ausführlich erklärt.

210
Ivan Zlatanov

Es ist nicht notwendig, den Timer zu stoppen, siehe Nice-Lösung in diesem Beitrag :

"Sie können den Timer weiterhin mit der Callback-Methode auslösen, aber Ihren nicht wiedereintrittsfähigen Code in einen Monitor einfassen .TryEnter/Exit. In diesem Fall müssen Sie den Timer nicht stoppen/erneut starten. Überlappende Aufrufe erhalten die Sperre nicht und kehren nicht sofort zurück."

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

Ist die Verwendung von System.Threading.Timer obligatorisch?

Wenn dies nicht der Fall ist, verfügt System.Timers.Timer über praktische Start () - und Stop () - Methoden (und eine AutoReset-Eigenschaft, die Sie auf false setzen können, sodass Stop () nicht benötigt wird und Sie nach dem Ausführen einfach Start () aufrufen).

8
Marco Mp

Ich würde einfach tun:

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
  }

Und ignorieren Sie den Periodenparameter, da Sie versuchen, die Periodizität selbst zu steuern.


Ihr ursprünglicher Code wird so schnell wie möglich ausgeführt, da Sie 0 für den Parameter dueTime angeben. Von Timer.Change :

Wenn dueTime gleich Null (0) ist, wird die Callback-Methode sofort aufgerufen.

 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)

wenn Sie gerne einen Loop-Thread verwenden ...

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.
0
Umka