web-dev-qa-db-fra.com

Est-ce que les minuteries C # s'écoulent sur un thread séparé?

Un délai System.Timers.Timer s'écoule-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, un objet est modifié. Disons qu'il faut beaucoup de temps pour modifier cet objet, par exemple 10 secondes. Est-il possible que je rencontre des collisions de threads dans ce scénario?

84
user113164

Pour System.Timers.Timer :

Voir réponse de Brian Gideon ci-dessous

Pour System.Threading.Timer :

Documentation MSDN sur les timers

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

Donc, en effet, le chronomètre s'écoule sur un autre fil.

54
Joren

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

Si SynchronizingObject est défini sur une instance ISynchronizeInvoke, l'événement Elapsed sera exécuté sur le thread hébergeant l'objet en cours de synchronisation. Habituellement, ces instances ISynchronizeInvoke ne sont autres que de simples anciennes instances Control et Form que nous connaissons tous. Ainsi, 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 l'événement System.Windows.Forms.Timer. Sinon, cela dépend vraiment de l'instance spécifique ISynchronizeInvoke qui a été utilisée.

Si SynchronizingObject est null, l'événement Elapsed est appelé sur un thread ThreadPool et il se comporte comme le System.Threading.Timer. En fait, il utilise en fait un System.Threading.Timer en coulisse et effectue le marshaling after il reçoit le rappel de la minuterie si nécessaire.

180
Brian Gideon

Chaque événement écoulé sera déclenché dans le même thread, sauf si un événement Elapsed précédent est toujours en cours d'exécution.

Donc, il gère la collision pour vous

essayez de mettre ceci 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 appelant et 6 et 12 déclenchent à partir de l'événement bg elapsed. Si vous supprimez le Thread.Sleep (2000); vous obtiendrez quelque chose comme ça

10
6
6
6
6

Depuis il n'y a pas de collisions.

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

21
Simon

Pour System.Timers.Timer, sur un 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 a tiré sur un intervalle de 5 secondes:

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

Donc, comme on le voit, OnDummyTimerFired est exécuté sur le thread Workers.

Non, complication supplémentaire - Si vous réduisez l’intervalle à 10 ms,

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

En effet, si l'exécution précédente de OnDummyTimerFired n'est pas effectuée lors du prochain tick, alors .NET créerait un nouveau thread pour effectuer ce travail.

"La classe System.Timers.Timer fournit un moyen simple de gérer ce dilemme: elle expose une propriété publique SynchronizingObject. Définir cette propriété comme une instance d'un Windows Form (ou un contrôle sur un formulaire Windows) garantira que le code de votre gestionnaire d’événements Elapsed s’exécute sur le même thread que celui sur lequel SynchronizingObject a été instancié. "

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

15
Swab.Jat

Si l'événement écoulé dure plus longtemps que l'intervalle, il créera un autre thread pour déclencher l'événement écoulé. Mais il existe une solution de contournement pour cette

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