Maison > développement back-end > Tutoriel C#.Net > Synchronisation .NET et EventWaitHandle asynchrone

Synchronisation .NET et EventWaitHandle asynchrone

大家讲道理
Libérer: 2017-04-11 14:05:40
original
2798 Les gens l'ont consulté


Dans l'article précédent nous avons évoqué Mutex et les protagonistes de cet article directement ou indirectement Hérite de WaitHandle :

WaitHandle propose plusieurs méthodes de synchronisation. Le blog précédent sur Mutex a déjà mentionné une WaitOne(), qui est une méthode d'instance. De plus, WaitHandle a trois autres méthodes

statiques pour la synchronisation. :

Dépendance du thread

Mutex, comme Monitor, a une dépendance au thread Nous l'avons déjà mentionné, seuls les threads qui ont obtenu le verrou

objet
    via Monitor.Enter()/TryEnter() peuvent appeler Pulse()/Wait(). /Exit(); de même, seuls les threads qui obtiennent la propriété Mutex peuvent exécuter la méthode ReleaseMutex(), sinon une exception sera levée. C'est ce qu'on appelle la dépendance aux threads.
  • En revanche, EventWaitHandle et ses classes dérivées AutoResetEvent et ManualResetEvent sont indépendantes du thread. N'importe quel thread peut signaler à EventWaitHandle de réveiller le thread bloqué dessus.

  • Le sémaphore qui sera mentionné dans le prochain article est également indépendant des threads.

Notification d'événement

EventWaitHandle, AutoResetEvent et ManualResetEvent ont tous un « Événement » dans leurs noms, mais ceci est la même chose que le propre mécanisme d'événement de net n'a rien à voir avec cela, il n'implique aucun délégué ou aucune procédure de

gestionnaire d'événement

. Par rapport au Monitor et au Mutex que nous avons rencontrés auparavant, qui nécessitent que les threads se disputent les « verrous », nous pouvons les comprendre comme des « événements » qui nécessitent que les threads attendent. Le thread se bloque en attendant que ces événements « se produisent ». Une fois "l'événement" terminé, le thread bloqué peut continuer à fonctionner après avoir reçu le signal. Afin de coopérer avec les trois méthodes statiques SingnalAndWait()/WailAny()/WaitAll() sur WaitHandle, EventWaitHandle fournit sa propre méthode unique pour terminer et redémarrer "Event" :

bool:Set() : version anglaise MSDN : définit l'état de l'événement sur signalé, permettant à un ou plusieurs threads en attente de continuer ; Défini sur l'état terminé, permettant à un ou plusieurs threads en attente de continuer. À première vue, « signalé » et « terminaison » ne semblent pas correspondre, mais à bien y réfléchir, les deux termes ne sont en réalité pas contradictoires. Si l'événement est en cours, bien sûr, il n'y a pas de "termination", alors
    les autres
      threads doivent attendre une fois l'événement terminé, alors l'événement est "terminé", nous envoyons donc un signal pour se réveiller ; le fil d'attente, donc le statut "signal envoyé" est également raisonnable. Deux petits détails :
  1. Qu'il s'agisse de la version chinoise ou anglaise, il est mentionné que cette méthode peut faire "continuer/Procéder" "un" ou "plusieurs" threads en attente (attention pas "se réveiller") . Cette méthode est donc similaire à Monitor.Pulse() et Monitor.PulseAll() en termes de « réveil ». Quant à savoir quand il est similaire à Pulse() et quand il est similaire à PulseAll(), lisez la suite.

  2. Cette méthode a une valeur de retour booléenne : true si l'opération réussit ; sinon, false. Cependant, MSDN ne nous indique pas quand l'exécution échouera. Vous pouvez uniquement demander à un Microsoft MVP.

  • bool:Reset() : Définit l'état de l'événement sur non signalé, provoquant le blocage des threads. Définit l'état de l'événement sur non signalé, provoquant le blocage des threads. De même, nous devons comprendre que « non signalé » et « non terminé » sont la même chose. Encore une fois, il existe toujours une valeur de retour absurde. La fonction de Reset() équivaut à rendre à nouveau l'événement "en cours", puis tous les threads de l'événement WaitOne()/WaitAll()/WaitAny()/SignalAndWait() seront à nouveau bloqués.

  • Constructeur

    Jetons un coup d'œil au constructeur le plus courant de EventWaitHandle Simple :

    • EventWaitHandle (Boolean initialState, mode EventResetMode) : initialise une nouvelle instance de la classe EventWaitHandle et spécifie si le handle d'attente est initialement dans un état terminé et s'il est réinitialiser automatiquement ou réinitialiser manuellement. La plupart du temps, nous utiliserons false dans le premier paramètre afin que la nouvelle instance passe par défaut à l'état "non terminé". Le deuxième paramètre EventResetMode est une énumération avec un total de deux valeurs :

    1. EventResetMode.AutoReset : Lorsque Set() est appelé, l'EventWaitHandle actuel est transféré vers À l'état terminé, s'il y a un thread bloqué sur le EventWaitHandle actuel, alors après avoir libéré un thread, le EventWaitHandle sera automatiquement réinitialisé (équivalent à appeler automatiquement Reset()) et transféré vers le non- l'état terminé à nouveau, et les threads bloqués restants (le cas échéant) continueront à se bloquer. Si aucun thread n'est bloqué après l'appel de Set(), alors EventWaitHandle restera dans l'état "terminé" jusqu'à ce qu'un thread essaie d'attendre l'événement. Après cela, EventWaitHandle sera automatiquement réinitialisé et bloqué. tous les sujets après ça.

    2. EventResetMode.ManualReset : Une fois terminé, EventWaitHandle libère tous les threads en attente avant la réinitialisation manuelle, c'est-à-dire Reset(). Il reste terminé jusqu'à ce que appelé.

    OK, maintenant nous pouvons clairement savoir quand Set() est similaire à Monitor.Pulse()/PulseAll() respectivement :

    • Lorsque EventWaitHandle fonctionne en mode AutoReset, Set() est similaire à Monitor.Pulse() en termes de fonction de réveil. Pour le moment, Set() ne peut réveiller qu’un des nombreux threads bloqués (s’il y en a plusieurs). Mais il y a quand même quelques différences entre les deux :

    1. La fonction de Set() n'est pas seulement de "réveiller" mais de "libérer", permettant au fil de continuer travailler (continuer) ; au contraire, le thread réveillé par Pulse() ne fait que rentrer dans l'état Running et participe à la compétition pour le verrouillage de l'objet. Personne ne peut garantir qu'il obtiendra le verrouillage de l'objet.

    2. L'état appelé de Pulse() ne sera pas maintenu. Par conséquent, si Pulse() est appelé alors qu'il n'y a aucun thread en attente, le prochain thread qui appelle Monitor.Wait() sera toujours bloqué, comme si Pulse() n'avait jamais été appelé. En d’autres termes, Monitor.Pulse() ne prend effet que lorsqu’il est appelé, contrairement à Set(), qui continuera jusqu’au prochain WaitXXX().

  • Lorsque la méthode Set() d'un EventWaitHandle fonctionnant en mode ManualReset est appelée, sa fonction de réveil est similaire à Monitor.PulseAll(). Tous les threads sont bloqués. recevez le signal et soyez réveillé. La différence entre les deux est exactement la même que ci-dessus.

  • Jetons un coup d'œil aux autres constructeurs de EventWaitHandle :

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name) : Nous avons déjà vu les deux premiers paramètres, et le nom du troisième paramètre est utilisé pour spécifier les événements de synchronisation à l'échelle du système nom. Oui, comme nous l'avons mentionné dans l'article Mutex, puisque la classe parent WaitHandle a la capacité de traverser des domaines de processus, comme Mutex, nous pouvons créer un EventWaitHandle global et l'utiliser plus tard pour les notifications inter-processus. Notez que le nom est toujours sensible à la casse et qu'il existe toujours des problèmes de préfixe de nom. Vous pouvez vous y référer. Cela équivaut à créer un EventWaitHandle local et sans nom lorsque le nom est null ou string vide. Toujours pareil, il est possible qu'une seule instance soit renvoyée pour représenter l'EventWaitHandle portant le même nom car il existe déjà un EventWaitHandle portant le même nom dans le système. Donc au final, toujours pareil, si vous avez besoin de savoir si cet EventWaitHandle a été créé par vous en premier, vous devez utiliser l'un des deux constructeurs suivants.

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out BooleancreatedNew) : CreatedNew est utilisé pour indiquer si l'EventWaitHandle a été créé avec succès, true indique le succès , false Indique qu'un événement portant le même nom existe déjà.

    • EventWaitHandle(Boolean initialState, EventResetMode mode, String name, out BooleancreatedNew, EventWaitHandleSecurity) : concernant les problèmes de sécurité, veuillez vérifier directement l'exemple sur ce constructeur. Les problèmes de sécurité de MutexEventWaitHandle global doivent faire l'objet d'une plus grande attention que ceux de Mutex, car il est possible que des pirates utilisent le même nom d'événement pour envoyer des signaux ou organiser vos threads, ce qui peut sérieusement nuire à votre logique métier.

    Démo MSDN


    using System;using System.Threading;public class Example
    {    // The EventWaitHandle used to demonstrate the difference    // between AutoReset and ManualReset synchronization events.    //    private static EventWaitHandle ewh;    // A counter to make sure all threads are started and    // blocked before any are released. A Long is used to show    // the use of the 64-bit Interlocked methods.    //    private static long threadCount = 0;    // An AutoReset event that allows the main thread to block    // until an exiting thread has decremented the count.    //    private static EventWaitHandle clearCount = 
            new EventWaitHandle(false, EventResetMode.AutoReset);
    
        [MTAThread]    public static void Main()
        {        // Create an AutoReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.AutoReset);        // Create and start five numbered threads. Use the        // ParameterizedThreadStart delegate, so the thread        // number can be passed as an argument to the Start 
            // method.
            for (int i = 0; i <= 4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        // When multiple threads use a 64-bit value on a 32-bit        // system, you must access the value through the        // Interlocked class to guarantee thread safety.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Release one thread each time the user presses ENTER,        // until all threads have been released.        //        while (Interlocked.Read(ref threadCount) > 0)
            {
                Console.WriteLine("Press ENTER to release a waiting thread.");
                Console.ReadLine();            // SignalAndWait signals the EventWaitHandle, which            // releases exactly one thread before resetting, 
                // because it was created with AutoReset mode. 
                // SignalAndWait then blocks on clearCount, to 
                // allow the signaled thread to decrement the count            // before looping again.            //            WaitHandle.SignalAndWait(ewh, clearCount);
            }
            Console.WriteLine();        // Create a ManualReset EventWaitHandle.        //        ewh = new EventWaitHandle(false, EventResetMode.ManualReset);        // Create and start five more numbered threads.        //        for(int i=0; i<=4; i++)
            {
                Thread t = new Thread(                new ParameterizedThreadStart(ThreadProc)
                );
                t.Start(i);
            }        // Wait until all the threads have started and blocked.        //        while (Interlocked.Read(ref threadCount) < 5)
            {
                Thread.Sleep(500);
            }        // Because the EventWaitHandle was created with        // ManualReset mode, signaling it releases all the        // waiting threads.        //        Console.WriteLine("Press ENTER to release the waiting threads.");
            Console.ReadLine();
            ewh.Set();
    
        }    public static void ThreadProc(object data)
        {        int index = (int) data;
    
            Console.WriteLine("Thread {0} blocks.", data);        // Increment the count of blocked threads.
            Interlocked.Increment(ref threadCount);        // Wait on the EventWaitHandle.        ewh.WaitOne();
    
            Console.WriteLine("Thread {0} exits.", data);        // Decrement the count of blocked threads.
            Interlocked.Decrement(ref threadCount);        // After signaling ewh, the main thread blocks on        // clearCount until the signaled thread has 
            // decremented the count. Signal it now.        //        clearCount.Set();
        }
    }
    Copier après la connexion


    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Étiquettes associées:
    source:php.cn
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Tutoriels populaires
    Plus>
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal