2009-05-02 9 views
12

Mam timer, który nie musi przetwarzać swojej obsługi zdarzeń w tym samym czasie. Lecz przetworzenie jednego Elimowanego zdarzenia może spowodować zakłócenie działania innych. Zaimplementowałem poniższe rozwiązanie, ale coś jest nie tak; wydaje się, że albo powinienem używać zegara inaczej, albo użyć innego obiektu w przestrzeni wątków. Czasomierz wydawał się najlepiej pasować, ponieważ muszę okresowo sprawdzać stan, ale czasami sprawdzanie trwa dłużej niż mój interwał. Czy to najlepszy sposób podejścia do tego?Jak zablokować licznik czasu podczas przetwarzania zdarzenia, które minęło?

// member variable 
private static readonly object timerLock = new object(); 
private bool found = false; 


// elsewhere 
timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds; 
timer.Elapsed = Timer_OnElapsed; 
timer.Start(); 


public void Timer_OnElapsed(object sender, ElapsedEventArgs e) 
{ 
    lock(timerLock) 
    { 
    if (!found) 
    { 
     found = LookForItWhichMightTakeALongTime(); 
    } 
    } 
} 
+2

To nie powinno być wspólnotowe wiki. – Samuel

+0

@Samuel - każde pytanie może być wiki społeczności, jeśli plakat jest pożądany. Najczęściej zadawane pytania określają tylko, że niektóre pytania powinny od początku stanowić wiki społecznościowe, ale nie ograniczają stosowania CW do jakichkolwiek pytań. – tvanfosson

+1

@tvanfosson: Dałem mu do zrozumienia, że ​​takie pytania nie powinny być wspólnotową wiki. – Samuel

Odpowiedz

13

Można ustawić AutoReset na wartość false, a następnie jawnie zresetować licznik czasu po zakończeniu pracy z nim. Oczywiście, jak sobie z tym radzisz, naprawdę zależy od tego, jak możesz oczekiwać, że timer będzie działał. Wykonanie tego w ten sposób pozwoliłoby zegarowi odejść od faktycznego określonego czasu (jak zatrzymanie i ponowne uruchomienie). Twój mechanizm pozwoliłby na uruchamianie każdego interwału, ale może to spowodować zaległości w nieobsłużonych zdarzeniach, które są teraz obsługiwane w pobliżu czasu wygaśnięcia licznika, który powoduje wywołanie programu obsługi.

timer.Interval = TimeSpan.FromSeconds(5).TotalMilliseconds; 
timer.Elapsed += Timer_OnElapsed; 
timer.AutoReset = false; 
timer.Start(); 


public void Timer_OnElapsed(object sender, ElapsedEventArgs e) 
{ 
    if (!found) 
    { 
     found = LookForItWhichMightTakeALongTime(); 
    } 
    timer.Start(); 
} 
+0

AutoReset i Elapsed są dla System.Threading.Timer, który nie jest bezpieczny dla pracy interfejsu użytkownika. – Samuel

+0

@Samuel - Upływające zdarzenie jest używane przez OP. Prawdopodobnie nie jest to aplikacja WinForm. – tvanfosson

8

Ja zwykle zatrzymać stoper podczas przetwarzania go, wprowadź try/finally blok i wznowić stoper po zakończeniu.

0
timer.enabled = false 

lub

timer.stop(); 

i

timer.enabled = true 

lub

timer.start(); 
-1

używać System.Threading.Timer jak w przykładzie

class Class1 
    { 
     static Timer timer = new Timer(DoSomething,null,TimeSpan.FromMinutes(1),TimeSpan.FromMinutes(1)); 

     private static void DoSomething(object state) 
     { 
      timer = null; // stop timer 

      // do some long stuff here 

      timer = new Timer(DoSomething, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); 
     } 



    } 
+1

Powinieneś zawsze wyrzucać instancje Timera, gdy skończysz z 'em –

+0

Timer będzie kontynuował wywoływanie metody wywołania zwrotnego, o ile moduł czyszczący nie wyczyścił go.Może to spowodować nieoczekiwane, niedeterministyczne efekty. –

1

Jeśli LookForItWhichMightTakeALongTime() będzie trwać długo, proponuję nie używając System.Windows.Forms.Timer ponieważ spowoduje to zablokowanie swój wątek UI i użytkownik może zabić aplikacja myśli, że ma zamrożone.

Co można użyć to BackgroundWorker (wraz z Timer, jeśli jest to wymagane).

public class MyForm : Form 
{ 
    private BackgroundWorker backgroundWorker = new BackgroundWorker(); 

    public MyForm() 
    { 
    InitializeComponents(); 
    backgroundWorker.DoWork += backgroundWorker_DoWork; 
    backgroundWorker.RunWorkerCompleted += 
           backgroundWorker_RunWorkerCompleted; 
    backgroundWorker.RunWorkerAsync(); 
    } 

    private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
    e.Result = LookForItWhichMightTakeALongTime(); 
    } 

    private void backgroundWorker_RunWorkerCompleted(object sender, 
              RunWorkerCompletedEventArgs e) 
    { 
    found = e.Result as MyClass; 
    } 
} 

I można nazwać RunWorkerAsync() od gdziekolwiek chcesz, nawet z Timer jeśli chcesz. Upewnij się tylko, czy działa BackgroundWorker, ponieważ wywołanie RunWorkerAsync() podczas działania spowoduje zgłoszenie wyjątku.

private void timer_Tick(object sender, EventArgs e) 
{ 
    if (!backgroundWorker.IsBusy) 
    backgroundWorker.RunWorkerAsync(); 
} 
Powiązane problemy