2012-01-30 25 views
11

List<System.Threading.Timer> var. Her Zamanlayıcı yapılandırılabilir bir aralıkta (varsayılan 10 dakika) harekete geçirir. Tümü aynı geri arama yöntemini çağırır (farklı bir parametreyle). Geri arama yönteminin işini tamamlamak için birkaç saniye sürebilir.System.Threading.Timer Geri Aramaların Programdan Çıkmadan Önce Tamamlanmasını Bekleyin

Program sonlandırıldığında, geri çağırma yönteminin yürütülmesinin derhal durdurulması gibi görünüyor (bunu doğru şekilde görüyor muyum?).

Şu anda yürütmekte olan geri arama yöntemlerinin, programdan çıkmadan önce tamamlanmasını nasıl zarif bir şekilde bekleyebilirim?

cevap

16

Sen WaitHandler parametre ile tüm zamanlayıcılar atın edebilirsiniz. geri çağırma yöntemi tamamlandığında bu işleyici yalnızca sinyallenecektir (Spec dediği gibi: "zamanlayıcısı olan tüm kuyruğa geri aramaları tamamlayana kadar bertaraf edilmez")

void WaitUntilCompleted(List<Timer> myTimers) 
{ 
    List<WaitHandle> waitHnd = new List<WaitHandle>(); 
    foreach (var timer in myTimers) 
    { 
     WaitHandle h = new AutoResetEvent(false); 
     if(!timer.Dispose(h)) throw new Exception("Timer already disposed."); 
     waitHnd.Add(h); 
    } 
    WaitHandle.WaitAll(waitHnd.ToArray()); 
} 

Düzenleme: @Peter atma yöntemi öneminin altını geri dönüş değeri. Zamanlayıcı zaten imha edildiğinde false değerini döndürür. Bu çözümlerin güvenilir kalmasını sağlamak için, daha önce elden çıkarma geri çağırma işleminin hala devam etmesine rağmen, geri çağırma işlemi bittiğinde böyle bir durumda kontrol edemeyeceğimiz için Timer zaten imha edildiğinde istisna atmak için değiştirdim!

+0

Sadece bunu test ettim ve @Peter haklı: Birden çok kez atıyorsanız bir kilitlenme alabilirsiniz. Cevabı doğru olanı. –

+0

@Peter çözümüne katılmıyorum, Düzenlememi oku. – Tomek

-2

Muhtemelen konsol uygulamasında çıkış beklemek mümkün değildir. Sen geri arama başladı ve çıkışta azaldığı her zaman artacaktır statik çalışan geri arama sayacı değişkeni oluşturabilirsiniz

: pencereler için

uygulama oluşturur. Tabii ki, bunu yaparken kilit kullanmalısın.

Ardından ilgili olayı ve sayacın 0 olmasını beklemeyi veya yalnızca çıkışı iptal etmeyi kontrol edebilirsiniz.

+0

gerçekten de ;-) http://msdn.microsoft.com/en-gb/library/system.console.cancelkeypress(v=vs olduğunu. 80) .aspx – Seb

+0

Tomek'in çözümü bir Konsol Uygulamasında düzgün çalışıyor. Çözümünüz de bir konsol uygulamasında çalışır.Uygulamanın Ana çıkışlardan önce 0'a ulaşması için bekleyen sayacı kontrol etmesi gerekiyordu. –

1

Beklemedeki tüm işlemler tamamlanana kadar ana iş parçacığını engellemek için ManualResetEvents kullanabilirsiniz. Eğer ayarlı başlangıç ​​durumuna sahip bir System.Threading.ManualResetEvent[] dizi olabilir tüm zamanlayıcılar en az bir kere, sonra yürütmek istiyorsanız örneğin

olmayan sinyal kodunuzda Yani bir yerde

Eğer zamanlayıcı kurulum olurdu ve waithandle başlatıldı.

// in main setup method.. 
int frequencyInMs = 600000; //10 mins 
Timer timer = new Timer(); 
timer.Elapsed += (s, e) => MyExecute(); 
myTimers.Add(timer) 

ManualResetEvent[] _waithandles = new ManualResetEvent[10]; 
_waithandles[0] = new ManualResetEvent(false); 

// Other timers ... 
timer = new Timer(); 
timer.Elapsed += (s, e) => MyOtherExecute(); 
myTimers.Add(timer)   
_waithandles[1] = new ManualResetEvent(false); 
// etc, and so on for all timers 

// then in each method that gets executed by the timer 
// simply set ManualReset event to signalled that will unblock it. 
private void MyExecute() 
{ 
    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 

// In your main before exiting, this will cause the main thread to wait 
// until all ManualResetEvents are set to signalled 
WaitHandle.WaitAll(_waithandles);  

sen ancak o zaman basitçe böyle bir şeye değiştirmek bitirmek için operasyonlarını bekleyen beklemek isteseydim:

_waithandles[0] = new ManualResetEvent(true); // initial state set to non blocking. 

private void MyExecute() 
{ 
    _waithandles[0].Reset(); // set this waithandle to block.. 

    // do all my logic then when done signal the manual reset event 
    _waithandles[0].Set(); 
} 
3

Tomek'in kabul ettiği cevap güzel ama tamamlanmamış. Dispose işlevi false değerini döndürürse, iş parçacığı zaten bittiğinden, tamamlanmasını beklemek gerekmediği anlamına gelir. Böyle bir durumda bir WaitHandle beklemek için bir girişimde bulunursanız, WaitAll asla geri dönmeyecek, böylece kendiniz uygulamanızı/iş parçacığını keyfi olarak donduran bir işlev oluşturdunuz. İşte

nasıl görünmelidir geçerli:

void WaitUntilCompleted(List<Timer> myTimers) 
    { 
     List<WaitHandle> waitHnd = new List<WaitHandle>(); 
     foreach (var timer in myTimers) 
     { 
      WaitHandle h = new AutoResetEvent(false); 
      if (timer.Dispose(h)) 
      { 
       waitHnd.Add(h); 
      } 
     } 
     WaitHandle.WaitAll(waitHnd.ToArray()); 
    } 
+1

Kabul et, Al, sadece Zamanlayıcıda bir kereden fazla çağrılırsa false döndürür. Timer'ın geri çağırma işleminin bitmiş olması, Getiri iade değerini etkilemez. – Tomek

+0

Geri çekilmediğinden emin olmalısınız, eğer tek bir çekim zamanlayıcısıysa, sonsuza kadar bekleyeceğinizden. –

İlgili konular