views:

1096

answers:

2

I am new to CSharp and Threading.

To be familiar with Monitor.Wait,Monitor.lock and Monitor.PulseAll,I framed a scenario described below.

"A FootballGround is being shared by different teams for practicing purpose. At any time only one team can use the ground for their practice. A team can use the ground for 30 minutes for their practice. Once the time reaches 25 minutes it should signal other threads that the ground is about to free after 5 minutes.When the ground is wet (enum has three values free,allotted,wet) no team is allowed to lock the ground and all should wait for 10 minutes”

Honestly speaking, I do not know how turn the description in to actual coding. Based on my understanding I designed the outline.

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

      }

   }

}

Here I do not know how to apply locks and signalls.kindly help me .

+2  A: 

The important thing to always remember with locking and mutithreaded applciations is that locking is only effective if all your code that accesses a locked resource plays by the same rules i.e if one thread can lock a resource, all other threads that can access that same resource should use locks before accessing that resource.

Monitor and Lock

The lock keyword is convenienve wrapper for the Monitor class. This means that lock(obj) is the same as Monitor.Enter(obj) (although Monitor has the added functionality to timeout after a period of time if it could not get the lock to the object).

Pulsing events and threads

When a number of threads are waiting to gain a lock on some resource, via code you can signal when the owner thread is completed with the resource. This is known as signalling or pulsing and can be accomplished via Monitor.Pulse, Monitor.PulseAll, ManualResetEvent.Set or even AutoResetEvent.Set.

Soccer Example

So your soccer example below would be coded to include thread locking as follows:

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

}

** Notes **

  • Use ManualResetEvent instead of lock and Monitor since we want direct control of when the state of the lock is pulsed to enable other threads to play a game of football.

  • Pass in a reference to the FootBallGrounds to each Team because each team will play on specific foot ball grounds and each football grounds could be occupied by another team

  • Pass in a reference to the current team playing on the FootBallGround because only one team can be playing on the grounds at a time.

  • Use ThreadPool.QueueUserWorkItem since it's more efficient at creating simple threads than us manually creating threads. Ideally we could use a Timer instance as well.

Mike J
Thanks Mike for Spending your valuable time :)
No problem at all!
Mike J
+3  A: 

In reality, your scenario doesn't hugely map to a lock - but we'll try anyway ;-p

I've tweaked the setup a bit; instead:

  • one entity can have the grounds at a time
  • when a team finishes, they tell the first person they see (if anyone is waiting)
  • when the cleaner finishes, they are noisy - so everyone notices them leave and tries to get on the pitch

Here's the code; note that they don't have the lock when using the grounds, as that would prevent other people from joining the queue for Pulse.

In reality, we could do all of this with just lock (not using Pulse at all), and just use the standard blocking behaviour. But this sample shows Pulse and PulseAll being used to re-activate threads when a condition is met.

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);
        }
    }    
}
Marc Gravell
Nice,Impeccable and Ingenious answer. :)