System.Threading.Timer C # içinde çalışmıyor gibi görünüyor. Her 3 saniyede çok hızlı çalışır


112

Bir zamanlayıcı nesnem var. Her dakika çalışmasını istiyorum. Özellikle, bir OnCallBackyöntem çalıştırmalı ve bir OnCallBackyöntem çalışırken devre dışı kalmalıdır . Bir OnCallBackyöntem bittiğinde, (a OnCallBack) bir zamanlayıcıyı yeniden başlatır.

İşte şu anda sahip olduğum şey:

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
}

Ancak, çalışmıyor gibi görünüyor. Her 3 saniyede çok hızlı çalışır. Bir dönem artırsa bile (1000 * 10). Görmezden geliyor gibi görünüyor1000 * 10

Neyi yanlış yaptım?


12
Kimden Timer.Change: "DueTime sıfır (0) ise, geri arama yöntemi hemen çağrılır.". Bana sıfır gibi görünüyor.
Damien_The_Unbeliever

2
Evet ama ne olmuş yani? bir de dönem var.
Alan Coromano

10
Peki ya bir de dönem varsa? Alıntılanan cümle, dönem değeri hakkında hiçbir iddiada bulunmaz. Yalnızca "bu değer sıfırsa, hemen geri aramayı başlatacağım" diyor.
Damien_The_Unbeliever

3
İlginç bir şekilde, hem bitişTime hem de süreyi 0 olarak ayarlarsanız, zamanlayıcı her saniye çalışır ve hemen başlar.
Kelvin

Yanıtlar:


230

Bu, System.Threading.Timer'ın doğru kullanımı değildir. Zamanlayıcıyı örneklediğinizde, hemen hemen her zaman aşağıdakileri yapmalısınız:

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

Bu, zamanlayıcıya aralık sona erdiğinde yalnızca bir kez işlem yapması talimatını verecektir. Ardından Geri Arama işlevinizde zamanlayıcıyı daha önce değil, iş tamamlandığında değiştirirsiniz. Misal:

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

Dolayısıyla, eşzamanlılık olmadığı için kilitleme mekanizmalarına gerek yoktur. Zamanlayıcı, sonraki aramayı + uzun süren işlemin süresini geçtikten sonra başlatır.

Zamanlayıcınızı tam olarak N milisaniyede çalıştırmanız gerekiyorsa, Kronometre'yi kullanarak uzun süren işlemin süresini ölçmenizi ve ardından uygun şekilde Değiştir yöntemini çağırmanızı öneririm:

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

NET yapan ve Jeffrey Richter'in kitabını okumamış CLR'yi kullanan herkesi şiddetle tavsiye ediyorum - C # aracılığıyla CLR , okuması mümkün olan en kısa sürede. Zamanlayıcılar ve iş parçacığı havuzları burada büyük ayrıntılarla açıklanmıştır.


6
Ben buna katılmıyorum private void Callback( Object state ) { // Long running operation _timer.Change( TIME_INTERVAL_IN_MILLISECONDS, Timeout.Infinite ); }. Callbackbir işlem tamamlanmadan yeniden çağrılabilir.
Alan Coromano

2
Demek istediğim, bunun Long running operationçok daha fazla zaman alabileceğiydi TIME_INTERVAL_IN_MILLISECONDS. O zaman ne olur?
Alan Coromano

32
Geri arama tekrar aranmayacaktır, konu budur. Bu yüzden Timeout.Infinite'ı ikinci bir parametre olarak geçirdik. Bu temelde zamanlayıcı için bir daha işaretlemeyin anlamına gelir. İşlemi tamamladıktan sonra onaylamak için yeniden planlayın.
Ivan Zlatanov

Çaylak burada iş parçacığı - ThreadPoolzamanlayıcıyı geçerseniz bunun bir ile yapılabileceğini düşünüyor musunuz? Belirli bir aralıkta bir işi yapmak için yeni bir iş parçacığının ortaya çıktığı ve tamamlandığında iş parçacığı havuzuna düşürüldüğü bir senaryo düşünüyorum.
jedd.ahyoung

2
System.Threading.Timer, adanmış bir iş parçacığı değil, iş parçacığı havuzunda geri çağrılarını yürüten bir iş parçacığı havuzu zamanlayıcısıdır. Zamanlayıcı geri arama rutinini tamamladıktan sonra, geri aramayı çalıştıran iş parçacığı havuza geri döner.
Ivan Zlatanov

14

Zamanlayıcıyı durdurmak gerekli değildir , bu gönderiden güzel çözümü görün :

"Zamanlayıcının geri arama yöntemini çalıştırmaya devam etmesine izin verebilir, ancak tekrar girişli olmayan kodunuzu bir Monitor. TryEnter / Exit'e sarabilirsiniz. Bu durumda zamanlayıcıyı durdurmanıza / yeniden başlatmanıza gerek yoktur; örtüşen çağrılar kilidi almayacak ve hemen geri dönmeyecektir."

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

benim durumum için değil. Zamanlayıcıyı tam olarak durdurmam gerekiyor.
Alan Coromano

Geri aramayı birden çok kez engellemeye mi çalışıyorsunuz? Eğer hayır ise ne başarmaya çalışıyorsun?
Ivan Leonenko 9'12

1. Geri aramayı birden çok kez engelleyin. 2. Çok fazla kez çalıştırmayı önleyin.
Alan Coromano

Bu tam olarak yaptığı şey. Nesne kilitliyse, özellikle de çok büyük bir aralığa sahipseniz, if ifadesinden hemen sonra döndüğü sürece # 2 fazla yük değildir.
Ivan Leonenko 9'12

1
Bu, kodun son çalıştırmadan sonra <aralık> 'dan daha az çağrılmayacağını garanti etmez (zamanlayıcının yeni bir tıklaması, bir önceki kene kilidi serbest bıraktıktan bir mikrosaniye sonra tetiklenebilir). Bunun katı bir gereklilik olup olmadığına bağlıdır (sorunun açıklamasından tamamen açık değildir).
Marco Mp

9

Kullanmak System.Threading.Timerzorunlu mu?

Değilse System.Timers.Timer, kullanışlı Start()ve Stop()yöntemlere sahiptir (ve AutoResetfalse olarak ayarlayabileceğiniz bir özellik, böylece Stop()gerekli değildir ve Start()çalıştırdıktan sonra yalnızca çağırırsınız ).


3
Evet, ama gerçek bir gereklilik olabilir ya da en çok kullanılan zamanlayıcı olduğu için zamanlayıcı seçildi. Ne yazık ki .NET,% 90 için üst üste binen ancak yine de (bazen ince bir şekilde) farklı olan tonlarca zamanlayıcı nesnesine sahiptir. Tabii eğer bir gereklilikse bu çözüm hiç geçerli değil.
Marco Mp

2
Gereğince belgeler : Systems.Timer sınıfı yalnızca .NET Framework mevcuttur. .NET Standart Kitaplığı'na dahil değildir ve .NET Core veya Evrensel Windows Platformu
NotAgain, "Reinstate Monica" diyor

3

Sadece yapardım:

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
  }

Ayrıca periyodik olanı kendiniz kontrol etmeye çalıştığınız için dönem parametresini yok sayın.


Parametre 0için belirlemeye devam ettiğiniz için orijinal kodunuz olabildiğince hızlı çalışıyor dueTime. Kimden Timer.Change:

DueTime sıfır (0) ise, geri arama yöntemi hemen çağrılır.


2
Zamanlayıcıyı atmak gerekli mi? Neden Change()yöntemi kullanmıyorsun ?
Alan Coromano

21
Zamanlayıcıyı her seferinde atmak kesinlikle gereksiz ve yanlıştır.
Ivan Zlatanov

0
 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)

içinde biraz döngü ipliği kullanmayı seviyorsanız ...

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.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.