Question Threading C #: Utilisation de Monitor.Wait, Lock et PulseAll


Je suis nouveau sur CSharp et Threading.

Pour se familiariser avec Monitor.Wait, Monitor.lock et Monitor.PulseAll, j'ai encadré un scénario décrit ci-dessous.

"Un terrain de football est partagé par différentes équipes à des fins de pratique. À tout moment, une seule équipe peut utiliser le terrain pour ses essais. Une équipe peut utiliser le terrain pendant 30 minutes. Une fois que le temps atteint 25 minutes, des fils que le sol est sur le point de libérer au bout de 5 minutes.Lorsque le sol est mouillé (enum a trois valeurs libres, attribuées, humide) aucune équipe n'est autorisée à verrouiller le sol et tous doivent attendre 10 minutes "

Honnêtement, je ne sais pas comment transformer la description en codage réel. Sur la base de ma compréhension, j'ai conçu le contour.

namespace ThreadingSimulation
 {

  // A Random Activity can be picked up from this enum by a team

         public enum RandomGroundStatus
         {
             free,
             allotted,
             Wet          
         }

class FootBallGround
{

    public void Playing(object obj)
    {
      // Here the name of the team which is using the  ground will be printed
      // Once the time is reached to 25 minnutes the active thread acquired
      // the lock will signal  other threads    

    }

   public void GroundCleaningInProgress(object obj)
   {

      // Ground cleaning is in progress all of you
      // wait for 10 minutes

   }

}    




 class Team
   {
     string teamName;  

      static void Main()
      {

        //select random value for GrandStatus from enum

       // if the ground is wet no team is allowed to get the
       // ground for 10 minutes  

        //if the ground is free "Team A" locks the ground

        // otherwise "Team B" locks the ground

      }

   }

}

Ici, je ne sais pas comment appliquer les verrous et les signaux. Aidez-moi.


15
2017-10-14 09:38


origine


Réponses:


En réalité, votre scénario ne correspond pas à un lock - mais nous essaierons quand même ;-p

J'ai un peu modifié la configuration. au lieu:

  • une entité peut avoir les motifs à la fois
  • lorsqu'une équipe termine, elle dit à la première personne qu'elle voit (si quelqu'un attend)
  • quand le nettoyeur finit, ils sont bruyants - donc toutes les personnes les remarque partir et tente de monter sur le terrain

Voici le code; notez qu'ils n'ont pas le verrou quand en utilisant les motifs, car cela empêcherait d'autres personnes de rejoindre la file d'attente pour Pulse.

En réalité, nous pourrions faire tout de ceci avec juste  lock (n'utilise pas Pulse du tout), et utilisez simplement le comportement de blocage standard. Mais cet exemple montre Pulse et PulseAll être utilisé pour réactiver les threads lorsqu'une condition est remplie.

using System;
using System.Threading;
interface IGroundUser
{
    bool Invoke(); // do your stuff; return true to wake up *everyone*
                   // afterwards, else false
}
class Team : IGroundUser
{
    private readonly string name;
    public Team(string name) { this.name = name; }
    public override string ToString() { return name; }
    public bool Invoke()
    {
        Console.WriteLine(name + ": playing...");
        Thread.Sleep(25 * 250);
        Console.WriteLine(name + ": leaving...");
        return false;
    }
}
class Cleaner : IGroundUser
{
    public override string ToString() {return "cleaner";}
    public bool Invoke()
    {
        Console.WriteLine("cleaning in progress");
        Thread.Sleep(10 * 250);
        Console.WriteLine("cleaning complete");
        return true;
    }
}
class FootBallGround
{
    static void Main()
    {
        var ground = new FootBallGround();
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Team("Team A")); });
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Team("Team B")); });
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Cleaner()); });
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Team("Team C")); });
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Team("Team D")); });
        ThreadPool.QueueUserWorkItem(delegate { ground.UseGrounds(new Team("Team E")); });
        Console.ReadLine();

    }
    bool busy;
    private readonly object syncLock = new object();
    public void UseGrounds(IGroundUser newUser)
    {
        // validate outside of lock
        if (newUser == null) throw new ArgumentNullException("newUser");
        // only need the lock when **changing** state
        lock (syncLock)
        {
            while (busy)
            {
                Console.WriteLine(newUser + ": grounds are busy; waiting...");
                Monitor.Wait(syncLock);
                Console.WriteLine(newUser + ": got nudged");
            }
            busy = true; // we've got it!
        }
        // do this outside the lock, allowing other users to queue
        // waiting for it to be free
        bool wakeAll = newUser.Invoke();

        // exit the game
        lock (syncLock)
        {
            busy = false;
            // wake up somebody (or everyone with PulseAll)
            if (wakeAll) Monitor.PulseAll(syncLock);
            else Monitor.Pulse(syncLock);
        }
    }    
}    

13
2017-10-14 11:39



La chose importante à retenir avec les applications bloquées et liées est que le verrouillage n’est efficace que si tout votre code qui accède à une ressource verrouillée joue selon les mêmes règles, par exemple si un thread peut verrouiller une ressource, tous les autres threads pouvant accéder à cette même ressource doivent utiliser des verrous avant d'accéder à cette ressource.

Surveiller et verrouiller

le lock le mot clé est convenienve wrapper pour le Monitor classe. Cela signifie que lock(obj) est le même que Monitor.Enter(obj) (bien que Monitor a la fonctionnalité ajoutée à expirer après une période de temps s'il n'a pas pu obtenir le verrou à l'objet).

Événements et fils pulsants

Lorsqu'un certain nombre de threads attendent un verrou sur une ressource, vous pouvez indiquer via le code lorsque le thread propriétaire est terminé avec la ressource. Ceci est connu comme signalisation ou pulser et peut être accompli via Monitor.Pulse, Monitor.PulseAll, ManualResetEvent.Set ou même AutoResetEvent.Set.

Exemple de football

Ainsi, votre exemple de football ci-dessous serait codé pour inclure le verrouillage de fil comme suit:

 namespace ThreadingSimulation
 {

   // A Random Activity can be picked up from this enum by a team

    public enum RandomGroundStatus
    {
        Free,
        Allotted,
        Wet          
    }

 class FootBallGround
 {
     private Team _currentTeam;

     // Set the initial state to true so that the first thread that 
     // tries to get the lock will succeed
     private ManualResetEvent _groundsLock = new ManualResetEvent(true);

     public bool Playing(Team obj)
     {
       // Here the name of the team which is using the  ground will be printed
       // Once the time is reached to 25 minutes the active thread the lock will
       // signal other threads    
       if (!_groundsLock.WaitOne(10))
         return false;

       _currentTeam = obj;

       // Reset the event handle so that no other thread can come into this method
       _groundsLock.Reset();    

       // Now we start a separate thread to "timeout" this team's lock 
       // on the football grounds after 25 minutes
       ThreadPool.QueueUserWorkItem(WaitForTimeout(25));                  
     }

    public void GroundCleaningInProgress(object obj)
    {

       // Ground cleaning is in progress all of you wait for 10 minutes

    }

    private void WaitForTimeout(object state)
    {
         int timeout = (int)state;

         // convert the number we specified into a value equivalent in minutes
         int milliseconds = timeout * 1000;
         int minutes = milliseconds * 60;

         // wait for the timeout specified 
         Thread.Sleep(minutes);

         // now we can set the lock so another team can play
         _groundsLock.Set();
     }
 }    

 class Team
  {
      string teamName;  
      FootBallGround _ground;

       public Team(string teamName, FootBallGround ground)
       {
          this.teamName = teamName;
          this._ground = ground;      
       }

       public bool PlayGame()
       {
            // this method returns true if the team has acquired the lock to the grounds
            // otherwise it returns false and allows other teams to access the grounds
            if (!_ground.Playing(this))
               return false;
            else
               return true;
       }
  }


  static void Main()
  {
         Team teamA = new Team();
         Team teamB = new Team();

         // select random value for GrandStatus from enum
         RandomGroundStatus status = <Generate_Random_Status>;

         // if the ground is wet no team is allowed to get the
         // ground for 10 minutes.
         if (status == RandomGroundStatus.Wet)
            ThreadPool.QueueUserWorkItem(WaitForDryGround);
         else
         {
             // if the ground is free, "Team A" locks the ground
             // otherwise "Team B" locks the ground

             if (status == RandomGroundStatus.Free)
             {
               if (!teamA.PlayGame())
                  teamB.PlayGame();
             }
          }
    }

}

** Remarques **

  • Utilisation ManualResetEvent au lieu de lock et Monitor puisque nous voulons un contrôle direct de quand l'état de la serrure est pulsé pour permettre aux autres threads de jouer à un jeu de football.

  • Passez une référence à la FootBallGrounds pour chaque Team parce que chaque équipe jouera sur des terrains spécifiques et que chaque terrain de football pourrait être occupé par une autre équipe

  • Transmettez une référence à l'équipe en train de jouer sur le FootBallGround car une seule équipe peut jouer sur le terrain à la fois.

  • Utilisation ThreadPool.QueueUserWorkItem car il est plus efficace de créer des threads simples que de créer manuellement des threads. Idéalement, nous pourrions utiliser un Timer exemple aussi.


5
2017-10-14 10:25