简体   繁体   English

C#线程:使用Monitor.Wait,Lock和PulseAll

[英]C# Threading: Using Monitor.Wait, Lock and PulseAll

I am new to CSharp and Threading. 我是CSharp和Threading的新手。

To be familiar with Monitor.Wait,Monitor.lock and Monitor.PulseAll,I framed a scenario described below. 为了熟悉Monitor.Wait,Monitor.lock和Monitor.PulseAll,我构建了一个下面描述的场景。

"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” “不同的球队正在为实践目的分享一个FootballGround。任何时候只有一个团队可以使用他们的练习场。一个团队可以使用地面练习30分钟。一旦时间达到25分钟,它应该发出信号通知其他人5分钟后地面即将释放的线程。当地面潮湿时(enum有三个值自由,分配,潮湿)没有任何队员可以锁定地面,所有人都应该等待10分钟“

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 . 在这里,我不知道如何应用锁和signalls.kindly帮助我。

In reality, your scenario doesn't hugely map to a lock - but we'll try anyway ;-p 实际上,您的场景并没有大量映射到lock - 但我们无论如何都会尝试;-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 . 请注意,在使用场地时他们没有锁,因为这会阻止其他人加入Pulse的队列。

In reality, we could do all of this with just lock (not using Pulse at all), and just use the standard blocking behaviour. 在现实中,我们可以做到这一切 lock (不使用Pulse的话),只是使用标准的阻塞行为。 But this sample shows Pulse and PulseAll being used to re-activate threads when a condition is met. 但是此示例显示PulsePulseAll用于在满足条件时重新激活线程。

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

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 ie if one thread can lock a resource, all other threads that can access that same resource should use locks before accessing that resource. 使用锁定和mutithreaded应用程序始终记住的重要一点是,只有当访问锁定资源的所有代码遵循相同规则时,锁定才有效,即如果一个线程可以锁定资源,则所有其他可以访问同一资源的线程应该在访问该资源之前使用锁。

Monitor and Lock 监控和锁定

The lock keyword is convenienve wrapper for the Monitor class. lock关键字是Conven类的Monitor类包装Monitor 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). 这意味着lock(obj)Monitor.Enter(obj)相同(尽管Monitor具有添加的功能,如果无法获取对象的锁定,则会在一段时间后超时)。

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 . 这称为信令脉冲 ,可以通过Monitor.PulseMonitor.PulseAllManualResetEvent.Set甚至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. 使用ManualResetEvent代替lockMonitor因为我们希望直接控制锁定状态何时发出脉冲以使其他线程能够进行足球比赛。

  • 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 将每个TeamFootBallGrounds参考传递给每支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. 传递给FootBallGround上当前球队的参考,因为一次只能有一支球队在球场上比赛。

  • Use ThreadPool.QueueUserWorkItem since it's more efficient at creating simple threads than us manually creating threads. 使用ThreadPool.QueueUserWorkItem因为它比我们手动创建线程更有效地创建简单线程。 Ideally we could use a Timer instance as well. 理想情况下,我们也可以使用Timer实例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM