neděle 14. září 2014

Časovače ve službách

Toto je jen zamyšlení, jak správně používat časovače pro opakované spouštění operací. Obvykle se to řeší takto:

private int interval = 3600000;
private Timer timer = new Timer(2000);

public void Start()
{
   this.timer.Elapsed += this.OnTimerElapsed;
   this.timer.Start();
}

private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
   this.timer.Stop();
   .....
   this.timer.Interval = this.interval;
   this.timer.Start();
}


Drobným nedostatkem tohoto kódu je fakt, že kód vlastně neběží v nastaveném intervalu, ale tento interval je prodloužen i o vlastní běh metody - například pokud je interval nastaven na 10 sekund a metoda běží  také 10 sekund, uplyne mezi dvěma voláními 20 sekund.

Takže tady je lepší řešeni- použije se System.Threading.Timer - ten umožňuje nastavit jak čas prvotního spuštění, tak interval mezi následnými voláními.

private Timer timer = null;
private readonly TimerSemaphore semaphore = new TimerSemaphore();

public void Start()
{
   int interval = 3600000;
   this.timer = new Timer(new TimerCallback(this.OnTimerElapsed), null, 200, interval);
}

private void OnTimerElapsed(object state)
{
   if (this.semaphore.IsRunning) return;

   do
   {
       ...
    } while (this.semaphore.ShouldBeRunning);
}

Objekt semaphore zabraňuje opětovnému spuštění metody, pokud tato již běží a navíc v případě, že dojde k tomuto opětovnému zavolání spustí metodu ihned znovu - takže pokud metoda běží pomaleji a doba běhu překročí nastavený interval, je metoda volána ihned znovu - na rozdíl od prvního kódu, kde se opětovně čeká.

Takto vypadá vlastní kód třídy TimerSemaphore:

public class TimerSemaphore
{
 private bool isRunning = false;
 private bool shouldBeRunning = false;
 private bool isStopped = false;
 private object locker = new object();
    
 public bool IsRunning
 { 
  get 
  { 
   lock(this.locker)
   {
    if (this.isStopped)
     return false;
    if(!this.isRunning)
    {
     this.isRunning = true;
     return false;
    }
    else
    {
     this.shouldBeRunning = true;
     return true;
    }
    
   }
  } 
 }

 public bool ShouldBeRunning
 {
  get
  {
   lock(this.locker)
   {
    if this.isStopped)
     return false;
    if(this.shouldBeRunning)
    {
     this.isRunning = true;
     this.shouldBeRunning = false;
     return true;
    }
    else
    {
     this.isRunning = false;
     return false;
    }
   }
  }
 }

 public void Stop()
 {
  lock(this.locker)
  {
   this.isRunning = false;
   this.shouldBeRunning = false;
   this.isStopped = true;
  }
 }
}





Žádné komentáře:

Okomentovat