Question

J'ai lu la documentation à ce sujet et je pense comprendre. Un AutoResetEvent est réinitialisé lorsque le code passe par event.WaitOne () , mais un ManualResetEvent ne le fait pas.

Est-ce correct?

Était-ce utile?

La solution

Oui. C'est comme la différence entre un péage et une porte. Le ManualResetEvent est la porte qui doit être fermée manuellement (réinitialisation). AutoResetEvent est un péage permettant à une voiture de passer et se fermant automatiquement avant que la suivante ne puisse passer.

Autres conseils

Imaginez que AutoResetEvent exécute WaitOne () et Reset () en une seule opération atomique.

La réponse courte est oui. La différence la plus importante est qu'un AutoResetEvent n'autorise qu'un seul thread en attente à continuer. En revanche, un événement ManualResetEvent permet aux threads, plusieurs à la fois, de continuer jusqu'à ce que vous leur demandiez de s'arrêter (réinitialisez-le).

  

Extrait du livre C # 3.0 Nutshell, de   Joseph Albahari

Threading en C # - Livre électronique gratuit

Un ManualResetEvent est une variante de AutoResetEvent. Il diffère en ce sens qu'il ne se réinitialise pas automatiquement après qu'un thread est passé dans un appel WaitOne, et fonctionne donc comme une porte: appelant Set ouvre la porte, permettant à n'importe quel nombre de threads passant par WaitOne à la porte; l'appel de Reset ferme la porte, ce qui peut éventuellement entraîner l'accumulation d'une file d'attente de serveurs jusqu'à la prochaine ouverture.

On pourrait simuler cette fonctionnalité avec un booléen "gateOpen". champ (déclaré avec le mot clé volatile) en combinaison avec " spin-sleep " & # 8211; en vérifiant à plusieurs reprises le drapeau, puis en dormant pendant une courte période.

ManualResetEvents sont parfois utilisés pour signaler qu'une opération particulière est terminée ou que l'initialisation d'un thread est terminée et qu'elle est prête à exécuter le travail.

J'ai créé des exemples simples pour clarifier la compréhension de ManualResetEvent par rapport à AutoResetEvent .

AutoResetEvent : supposons que vous ayez 3 threads de production. Si l'un de ces threads appelle WaitOne () , tous les 2 autres threads arrêteront l'exécution et attendront le signal. Je suppose qu'ils utilisent WaitOne () . C'est comme; si je ne travaille pas, personne ne travaille. Dans le premier exemple, vous pouvez voir que

autoReset.Set();
Thread.Sleep(1000);
autoReset.Set();

Lorsque vous appelez Set () , tous les threads fonctionnent et attendent le signal. Après 1 seconde, j'envoie un deuxième signal et ils s'exécutent et attendent ( WaitOne () ). Pensez à ces gars-là qui sont des joueurs d’équipe de football et si un joueur dit que j’attendrai que le responsable m’appelle, les autres attendent jusqu’à ce que le responsable leur dise de continuer ( Set () )

public class AutoResetEventSample
{
    private AutoResetEvent autoReset = new AutoResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        autoReset.Set();
        Thread.Sleep(1000);
        autoReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            autoReset.WaitOne();
        }
    }
}

Dans cet exemple, vous pouvez voir clairement que lorsque vous frappez Set () pour la première fois, tous les threads disparaissent, puis après 1 seconde, il est signalé à tous les threads d'attendre! Dès que vous les définissez à nouveau, peu importe qu'ils appellent WaitOne () , ils continuent de fonctionner car vous devez appeler manuellement Reset () pour les arrêter tous.

manualReset.Set();
Thread.Sleep(1000);
manualReset.Reset();
Console.WriteLine("Press to release all threads.");
Console.ReadLine();
manualReset.Set();

Il s’agit davantage de la relation arbitre / joueurs, peu importe le joueur blessé et attendre que les autres joueurs continuent à travailler. Si l'arbitre dit d'attendre ( Réinitialiser () ), tous les joueurs attendront le prochain signal.

public class ManualResetEventSample
{
    private ManualResetEvent manualReset = new ManualResetEvent(false);

    public void RunAll()
    {
        new Thread(Worker1).Start();
        new Thread(Worker2).Start();
        new Thread(Worker3).Start();
        manualReset.Set();
        Thread.Sleep(1000);
        manualReset.Reset();
        Console.WriteLine("Press to release all threads.");
        Console.ReadLine();
        manualReset.Set();
        Console.WriteLine("Main thread reached to end.");
    }

    public void Worker1()
    {
        Console.WriteLine("Entered in worker 1");
        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker1 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker2()
    {
        Console.WriteLine("Entered in worker 2");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker2 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
    public void Worker3()
    {
        Console.WriteLine("Entered in worker 3");

        for (int i = 0; i < 5; i++) {
            Console.WriteLine("Worker3 is running {0}", i);
            Thread.Sleep(2000);
            manualReset.WaitOne();
        }
    }
}

autoResetEvent.WaitOne ()

est similaire à

try
{
   manualResetEvent.WaitOne();
}
finally
{
   manualResetEvent.Reset();
}

en tant qu'opération atomique

OK, normalement, il n'est pas conseillé d'ajouter 2 réponses dans le même fil, mais je ne voulais pas modifier / supprimer ma réponse précédente, car cela pourrait aider d'une autre manière.

Maintenant, j'ai créé l'extrait de code ci-dessous, beaucoup plus complet et facile à comprendre, pour l'application de la console d'exécution.

Exécutez simplement les exemples sur deux consoles différentes et observez le comportement. Vous aurez une idée beaucoup plus claire de ce qui se passe dans les coulisses.

Evénement de réinitialisation manuelle

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class ManualResetEventSample
    {
        private readonly ManualResetEvent _manualReset = new ManualResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call ManualResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call ManualResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _manualReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("It ran one more time. Why? Even Reset Sets the state of the event to nonsignaled (false), causing threads to block, this will initial the state, and threads will run again until they WaitOne().");
            Thread.Sleep(10000);
            Console.WriteLine();
            Console.WriteLine("This will go so on. Everytime you call Set(), ManualResetEvent will let ALL threads to run. So if you want synchronization between them, consider using AutoReset event, or simply user TPL (Task Parallel Library).");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker1 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker2 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 10; i++)
            {
                Console.WriteLine("Worker3 is running {0}/10. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(5000);
                // this gets blocked until _autoReset gets signal
                _manualReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

 Sortie d'événement de réinitialisation manuelle

Evénement de réinitialisation automatique

using System;
using System.Threading;

namespace ConsoleApplicationDotNetBasics.ThreadingExamples
{
    public class AutoResetEventSample
    {
        private readonly AutoResetEvent _autoReset = new AutoResetEvent(false);

        public void RunAll()
        {
            new Thread(Worker1).Start();
            new Thread(Worker2).Start();
            new Thread(Worker3).Start();
            Console.WriteLine("All Threads Scheduled to RUN!. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("Main Thread is waiting for 15 seconds, observe 3 thread behaviour. All threads run once and stopped. Why? Because they call WaitOne() internally. They will wait until signals arrive, down below.");
            Thread.Sleep(15000);
            Console.WriteLine("1- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("2- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("3- Main will call AutoResetEvent.Set() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Set();
            Thread.Sleep(2000);
            Console.WriteLine("4- Main will call AutoResetEvent.Reset() in 5 seconds, watch out!");
            Thread.Sleep(5000);
            _autoReset.Reset();
            Thread.Sleep(2000);
            Console.WriteLine("Nothing happened. Why? Becasuse Reset Sets the state of the event to nonsignaled, causing threads to block. Since they are already blocked, it will not affect anything.");
            Thread.Sleep(10000);
            Console.WriteLine("This will go so on. Everytime you call Set(), AutoResetEvent will let another thread to run. It will make it automatically, so you do not need to worry about thread running order, unless you want it manually!");
            Thread.Sleep(5000);
            Console.WriteLine("Main thread reached to end! ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);

        }

        public void Worker1()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker1 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker1 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker2()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker2 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker2 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
        public void Worker3()
        {
            for (int i = 1; i <= 5; i++)
            {
                Console.WriteLine("Worker3 is running {0}/5. ThreadId: {1}.", i, Thread.CurrentThread.ManagedThreadId);
                Thread.Sleep(500);
                // this gets blocked until _autoReset gets signal
                _autoReset.WaitOne();
            }
            Console.WriteLine("Worker3 is DONE. ThreadId: {0}", Thread.CurrentThread.ManagedThreadId);
        }
    }

}

 Sortie d'événement de réinitialisation automatique

Oui. C'est absolument correct.

Vous pouvez voir ManualResetEvent comme un moyen d’indiquer un état. Quelque chose est allumé (réglé) ou éteint (réinitialisé). Un événement d'une certaine durée. Tout thread en attente de cet état peut continuer.

Un AutoResetEvent est plus comparable à un signal. Une indication unique que quelque chose est arrivé. Une occurrence sans durée. Généralement, mais pas nécessairement, le "quelque chose" cela est arrivé est petit et doit être géré par un seul thread - par conséquent, la réinitialisation automatique après qu'un seul thread a consommé l'événement.

Oui, c'est ça.

Vous pouvez vous faire une idée en utilisant ces deux-là.

Si vous devez indiquer que vous avez terminé votre travail et que d'autres (threads) peuvent attendre, vous devez utiliser ManualResetEvent.

Si vous devez disposer d'un accès exclusif mutuel à une ressource, vous devez utiliser AutoResetEvent.

AutoResetEvent conserve une variable booléenne en mémoire. Si la variable booléenne est fausse, elle bloque le thread et si la variable booléenne est vraie, elle débloque le thread.

Lorsque nous instancions un objet AutoResetEvent, nous passons la valeur par défaut de valeur booléenne dans le constructeur. Vous trouverez ci-dessous la syntaxe d'instancier un objet AutoResetEvent.

AutoResetEvent autoResetEvent = new AutoResetEvent(false);

Méthode WaitOne

Cette méthode bloque le thread actuel et attend le signal par un autre thread. La méthode WaitOne place le thread en cours dans un état de thread en veille. La méthode WaitOne renvoie true si elle reçoit le signal, sinon elle renvoie false.

autoResetEvent.WaitOne();

La seconde surcharge de la méthode WaitOne attend le nombre de secondes spécifié. S'il n'obtient aucun signal, le thread continue son travail.

static void ThreadMethod()
{
    while(!autoResetEvent.WaitOne(TimeSpan.FromSeconds(2)))
    {
        Console.WriteLine("Continue");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }

    Console.WriteLine("Thread got signal");
}

Nous avons appelé la méthode WaitOne en passant les 2 secondes comme arguments. Dans la boucle while, il attend le signal pendant 2 secondes, puis continue son travail. Lorsque le thread a reçu le signal, WaitOne renvoie true, quitte la boucle et affiche le "Signal obtenu par le thread".

Définir la méthode

La méthode AutoResetEvent Set a envoyé le signal au thread en attente pour qu'il poursuive son travail. Vous trouverez ci-dessous la syntaxe d'appel de la méthode Set.

autoResetEvent.Set();

ManualResetEvent conserve une variable booléenne en mémoire. Lorsque la variable booléenne est fausse, elle bloque tous les threads et lorsque la variable booléenne est vraie, elle débloque tous les threads.

Lorsque nous instancions un ManualResetEvent, nous l'initialisons avec une valeur booléenne par défaut.

ManualResetEvent manualResetEvent = new ManualResetEvent(false);

Dans le code ci-dessus, nous initialisons ManualResetEvent avec une valeur false, ce qui signifie que tous les threads qui appellent la méthode WaitOne bloquent jusqu'à ce qu'un thread appelle la méthode Set ().

Si nous initialisons ManualResetEvent avec une valeur vraie, tous les threads qui appellent la méthode WaitOne ne se bloqueront pas et seront libres de continuer.

Méthode WaitOne

Cette méthode bloque le thread actuel et attend le signal par un autre thread. Elle renvoie true si elle reçoit un signal, sinon false.

La syntaxe d'appel de la méthode WaitOne est décrite ci-dessous.

manualResetEvent.WaitOne();

Dans la seconde surcharge de la méthode WaitOne, nous pouvons spécifier l'intervalle de temps jusqu'à ce que le thread actuel attend le signal. Si, dans le temps, interne, il ne reçoit pas de signal, il renvoie false et passe à la ligne suivante de la méthode.

Ci-dessous, la syntaxe d'appel de la méthode WaitOne avec un intervalle de temps.

bool isSignalled = manualResetEvent.WaitOne(TimeSpan.FromSeconds(5));

Nous avons spécifié 5 secondes dans la méthode WaitOne. Si l'objet manualResetEvent ne reçoit pas de signal entre 5 secondes, il affecte la valeur false à la variable isSignalled.

Définir la méthode

Cette méthode est utilisée pour envoyer le signal à tous les threads en attente. Méthode Set () définit la variable booléenne de l'objet ManualResetEvent sur true. Tous les threads en attente sont débloqués et vont plus loin.

La syntaxe de la méthode Set () est indiquée ci-dessous.

manualResetEvent.Set();

Méthode de réinitialisation

Une fois que nous avons appelé la méthode Set () sur l'objet ManualResetEvent, sa valeur booléenne reste vraie. Pour réinitialiser la valeur, nous pouvons utiliser la méthode Reset (). La méthode de réinitialisation modifie la valeur booléenne en false.

La syntaxe d'appel de la méthode de réinitialisation est la suivante.

manualResetEvent.Reset();

Nous devons immédiatement appeler la méthode Reset après avoir appelé la méthode Set si nous voulons envoyer le signal aux threads plusieurs fois.

Si vous voulez comprendre AutoResetEvent et ManualResetEvent, vous devez comprendre non pas le threading, mais les interruptions!

.NET souhaite créer la programmation de bas niveau la plus éloignée possible.

Une interruption est un élément utilisé dans la programmation de bas niveau qui équivaut à un signal qui devient faible (ou vice-versa). Lorsque cela se produit, le programme interrompt son exécution normale et déplace le pointeur d'exécution sur la fonction qui gère cet événement .

La première chose à faire lorsqu'une interruption se produit est de réinitialiser son état, car le matériel fonctionne de cette manière:

  1. une broche est connectée à un signal et le matériel l'écoute (le signal ne peut avoir que deux états).
  2. si le signal change, cela signifie que quelque chose s'est passé et que le matériel a mis une variable de mémoire à l'état qui s'est passé (et qu'il en reste ainsi même si le signal change à nouveau).
  3. le programme remarque que la variable change d'état et déplace l'exécution vers une fonction de traitement.
  4. La première chose à faire, pour pouvoir réécouter cette interruption, est de réinitialiser cette variable de mémoire à l'état non passé.

C'est la différence entre ManualResetEvent et AutoResetEvent.
Si un événement ManualResetEvent se produit et que je ne le réinitialise pas, il ne pourra plus être écouté la prochaine fois.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top