2009-09-16 10 views
72

Un System.Timers.Timer trascorre su un thread separato rispetto al thread che lo ha creato?I timer C# vengono trascorsi su un thread separato?

Diciamo che ho una lezione con un timer che scatta ogni 5 secondi. Quando il timer scocca, nel metodo trascorso, alcuni oggetti vengono modificati. Diciamo che ci vuole molto tempo per modificare questo oggetto, come 10 secondi. È possibile che mi imbatterò in collisioni di thread in questo scenario?

+0

Ciò potrebbe portare a problemi. Si noti che, in generale, i thread del threadpool non sono progettati per processi a esecuzione prolungata. –

risposta

50

Per System.Timers.Timer:

Vedi Brian Gideon's answer below

Per System.Threading.Timer:

MSDN Documentation on Timers stati:

Lo System.Threading.Timer classe fa 012 I callbacksu thread ThreadPool e non utilizzano affatto il modello di eventi.

Quindi, in effetti, il timer scade su una filettatura diversa.

+19

Vero, ma questa è una classe completamente diversa. L'OP ha chiesto informazioni sulla classe System.Timers.Timer. –

+0

Oh, hai ragione. http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx dice "L'evento Elapsed viene generato su un thread ThreadPool." Stessa conclusione da lì suppongo. – Joren

+4

Beh, sì, ma non è proprio così semplice. Vedi la mia risposta. –

16

Ogni evento trascorso si attiva nello stesso thread, a meno che non sia ancora in esecuzione un precedente trascorso.

Così gestisce la collisione per voi

provare a mettere questo in una console

static void Main(string[] args) 
{ 
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); 
    var timer = new Timer(1000); 
    timer.Elapsed += timer_Elapsed; 
    timer.Start(); 
    Console.ReadLine(); 
} 

static void timer_Elapsed(object sender, ElapsedEventArgs e) 
{ 
    Thread.Sleep(2000); 
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId); 
} 

otterrete qualcosa di simile

10 
6 
12 
6 
12 

dove 10 è il thread chiamante e 6 e 12 stanno sparando dall'evento trascorso bg. Se si rimuove il Thread.Sleep (2000); Otterrai qualcosa di simile al

10 
6 
6 
6 
6 

Poiché non ci sono collisioni.

Ma questo ti lascia ancora un problema. se stai sparando l'evento ogni 5 secondi e ci vogliono 10 secondi per modificarlo hai bisogno di un po 'di blocco per saltare alcune modifiche.

+7

L'aggiunta di un 'timer.Stop()' all'inizio del metodo dell'evento Elapsed e quindi un 'timer.Start()' alla fine del metodo dell'evento Elapsed manterrà l'evento Elapsed in collisione. –

+2

Non è necessario inserire timer.Stop() è sufficiente definire il timer.AutoReset = false; quindi fai timer.Avvio() dopo aver elaborato l'evento. Penso che questo sia un modo migliore per evitare le collisioni. –

165

Dipende. Il System.Timers.Timer ha due modalità di funzionamento.

Se SynchronizingObject è impostato a un ISynchronizeInvoke un'istanza allora l'evento Elapsed eseguirà sul filo che ospita l'oggetto di sincronizzazione. Di solito queste istanze ISynchronizeInvoke non sono nient'altro che semplici vecchie Control e Form istanze che tutti noi conosciamo. In tal caso, l'evento Elapsed viene richiamato sul thread dell'interfaccia utente e si comporta in modo simile allo System.Windows.Forms.Timer. In caso contrario, dipende in realtà dall'istanza specifica ISynchronizeInvoke utilizzata.

Se SynchronizingObject è nullo allora l'evento Elapsed viene richiamato su un filo ThreadPool e si comporta simile al System.Threading.Timer.Infatti, in realtà utilizza uno System.Threading.Timer dietro le quinte e esegue l'operazione di marshalling dopo, se necessario, riceve la richiamata del timer.

+1

Ottima risposta, grazie per questo. –

+3

Se si desidera che il callback del timer venga eseguito su un nuovo thread, si dovrebbe usare un 'System.Threading.Timer' o 'System.Timers.Timer'? – CJ7

+0

@ cj7: O uno può farlo. –

11

Se l'evento trascorso richiede più tempo dell'intervallo, verrà creato un altro thread per aumentare l'evento trascorso. Ma v'è una soluzione per questo

static void timer_Elapsed(object sender, ElapsedEventArgs e)  
{  
    try 
    { 
     timer.Stop(); 
     Thread.Sleep(2000);   
     Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);  
    } 
    finally 
    { 
    timer.Start(); 
    } 
} 
+0

+1 Risposta semplice e pulita, ma non funzionerà sempre. Piuttosto si dovrebbe usare il lock o la proprietà SynchronizingObject del timer. – RollerCosta

10

Per System.Timers.Timer, sul filo separato, se SynchronizingObject non è impostato.

static System.Timers.Timer DummyTimer = null; 

    static void Main(string[] args) 
    { 
     try 
     { 

      Console.WriteLine("Main Thread Id: " + System.Threading.Thread.CurrentThread.ManagedThreadId); 

      DummyTimer = new System.Timers.Timer(1000 * 5); // 5 sec interval 
      DummyTimer.Enabled = true; 
      DummyTimer.Elapsed += new System.Timers.ElapsedEventHandler(OnDummyTimerFired); 
      DummyTimer.AutoReset = true; 

      DummyTimer.Start(); 

      Console.WriteLine("Hit any key to exit"); 
      Console.ReadLine(); 
     } 
     catch (Exception Ex) 
     { 
      Console.WriteLine(Ex.Message); 
     } 

     return; 
    } 

    static void OnDummyTimerFired(object Sender, System.Timers.ElapsedEventArgs e) 
    { 
     Console.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); 
     return; 
    } 

uscita si vedrebbe se DummyTimer sparato sulla intervallo di 5 secondi:

Main Thread Id: 9 
    12 
    12 
    12 
    12 
    12 
    ... 

Quindi, come si è visto, OnDummyTimerFired viene eseguito su filo lavoratori.

No, un'ulteriore complicazione - Se si riduce l'intervallo di dire 10 ms,

Main Thread Id: 9 
    11 
    13 
    12 
    22 
    17 
    ... 

Questo perché se l'esecuzione di prev OnDummyTimerFired non viene fatto quando la prossima tick viene licenziato, quindi .NET creerebbe una nuova filo per fare questo lavoro.

Complementare ulteriormente, "La classe System.Timers.Timer fornisce un modo semplice per affrontare questo dilemma: espone una proprietà pubblica SynchronizingObject. Impostare questa proprietà su un'istanza di un Windows Form (o un controllo su un Windows Form) assicurerà che il codice nel gestore eventi Elapsed venga eseguito sullo stesso thread su cui è stato creato l'istanza di SynchronizingObject. "

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx#S2

+0

Questo è un buon esempio. Non lo sapevo fino ad ora e ho bisogno di impostare alcune serrature qua e là. Perfezionare. –

Problemi correlati