2009-09-16 7 views
72

Un System.Timers.Timer s'exécute-t-il sur un thread distinct du thread qui l'a créé? Disons que j'ai une classe avec une minuterie qui se déclenche toutes les 5 secondes. Lorsque la minuterie se déclenche, dans la méthode écoulée, certains objets sont modifiés. Disons qu'il faut beaucoup de temps pour modifier cet objet, comme 10 secondes. Est-il possible que je vais rencontrer des collisions de thread dans ce scénario?Les temporisateurs C# se déroulent-ils sur un thread séparé?

+0

Cela pourrait conduire à des problèmes. Notez qu'en général, les threads de pool de threads ne sont pas conçus pour les processus de longue durée. –

Répondre

50

Pour System.Timers.Timer:

Voir Brian Gideon's answer below

Pour System.Threading.Timer:

MSDN Documentation on Timers états:

La classe System.Threading.Timer fait les rappels sur un thread ThreadPool et n'utilisent pas du tout le modèle d'événement.

Ainsi, le temporisateur s'écoule sur un thread différent.

+19

C'est vrai, mais c'est une classe complètement différente. L'OP a posé des questions sur la classe System.Timers.Timer. –

+0

Oh, vous avez raison. http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx indique "L'événement Elapsed est déclenché sur un thread ThreadPool." Même conclusion à partir de là je suppose. – Joren

+4

Eh bien, oui, mais ce n'est pas si simple. Vois ma réponse. –

16

Chaque événement écoulé se déclenche dans le même thread à moins qu'un précédent Elapsed ne soit encore en cours d'exécution.

Il gère la collision pour vous

essayer de mettre cela dans une 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); 
} 

vous obtiendrez quelque chose comme ça

10 
6 
12 
6 
12 

où 10 est le fil d'appel et 6 et 12 tirent de l'événement écoulé bg. Si vous supprimez le Thread.Sleep (2000); vous obtiendrez quelque chose comme ceci

10 
6 
6 
6 
6 

Comme il n'y a pas de collisions.

Mais cela vous laisse toujours un problème. Si vous lancez l'événement toutes les 5 secondes et qu'il faut 10 secondes pour éditer, vous avez besoin d'un verrouillage pour ignorer certaines modifications.

+7

L'ajout de 'timer.Stop()' au début de la méthode d'événement Elapsed, puis un 'timer.Start()' à la fin de la méthode d'événement Elapsed empêcheront l'événement Elapsed d'entrer en collision. –

+2

Vous n'avez pas besoin de mettre timer.Stop() vous avez juste besoin de définir timer.AutoReset = false; alors vous faites timer.Start() après avoir traité l'événement. Je pense que c'est une meilleure façon d'éviter les collisions. –

165

Cela dépend. Le System.Timers.Timer a deux modes de fonctionnement.

Si SynchronizingObject est défini sur une instance ISynchronizeInvoke, l'événement Elapsed s'exécutera sur le thread hébergeant l'objet de synchronisation. Habituellement, ces instances ISynchronizeInvoke ne sont rien d'autre que de simples anciennes instances Control et Form que nous connaissons tous. Dans ce cas, l'événement Elapsed est appelé sur le thread d'interface utilisateur et se comporte de la même manière que le System.Windows.Forms.Timer. Sinon, cela dépend vraiment de l'instance spécifique ISynchronizeInvoke qui a été utilisée.

Si SynchronizingObject est nul alors l'événement Elapsed est invoquée sur un fil ThreadPool et il se comporte comme l'System.Threading.Timer.En fait, il utilise en fait un System.Threading.Timer dans les coulisses et fait l'opération de marshaling après il reçoit le rappel de minuterie si nécessaire.

+1

Bonne réponse, merci pour cela. –

+3

Si vous vouliez que le rappel du temporisateur s'exécute sur un nouveau thread, devriez-vous utiliser un System.Threading.Timer ou un System.Timers.Timer? – CJ7

+0

@ cj7: L'un ou l'autre peut le faire. –

11

Si l'événement écoulé prend plus de temps que l'intervalle, il crée un autre thread pour augmenter l'événement écoulé. Mais il existe une solution de contournement pour cela

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

+1 Réponse simple et nette, mais cela ne fonctionnera pas toujours. Il faut plutôt utiliser la propriété lock ou SynchronizingObject de timer. – RollerCosta

10

Pour System.Timers.Timer, sur thread distinct, si SynchronizingObject n'est pas défini.

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

sortie vous verriez si DummyTimer tiré sur l'intervalle de 5 secondes:

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

Ainsi, comme on le voit, OnDummyTimerFired est exécuté sur fil travailleurs.

Non, complication supplémentaire - Si vous réduisez l'intervalle dire 10 ms,

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

En effet, si l'exécution précédente de OnDummyTimerFired ne se fait pas lors de son prochain tick est tiré, alors .NET créeraient une nouvelle thread pour faire ce travail. Compléter les choses davantage, "La classe System.Timers.Timer fournit un moyen simple de résoudre ce dilemme: elle expose une propriété publique SynchronizingObject. Windows Form) garantira que le code dans votre gestionnaire d'événements Elapsed s'exécute sur le même thread sur lequel le SynchronizingObject a été instancié. "

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

+0

C'est un bon exemple. Je ne savais pas jusqu'à maintenant et j'ai besoin de mettre des verrous ici et là. Parfait. –

Questions connexes