<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      并發(fā)編程 - 線程同步(七)之互斥鎖Monitor

      通過前面對鎖lock的基本使用以及注意事項的學(xué)習(xí),相信大家對鎖的同步機(jī)制有了大致了解,今天我們將繼續(xù)學(xué)習(xí)——互斥鎖Monitor。

      lock是C#語言中的關(guān)鍵字,是語法糖,lock語句最終會由C#編譯器解析成Monitor類實現(xiàn)相關(guān)語句。

      例如以下lock語句:

      lock (obj)
      {
          //同步代碼塊
      }
      

      最終會被解析成以下代碼:

      Monitor.Enter(obj);
      try
      {
          //同步代碼塊
      }
      finally
      {
          Monitor.Exit(obj);
      }
      

      lock關(guān)鍵字簡潔且易于使用,而Monitor類 則功能強(qiáng)大,能夠提供比lock關(guān)鍵字更細(xì)粒度、更靈活的控制以及更多的功能。

      因為lock關(guān)鍵字是Monitor類的語法糖,因此lock關(guān)鍵字面臨的問題,Monitor類同樣也會面臨。當(dāng)然也會存在一些Monitor類特有的問題。

      下面我們一起詳細(xì)學(xué)習(xí)Monitor類的注意事項以及實現(xiàn)一個簡單的生產(chǎn)者-消費(fèi)者模式示例代碼。

      01、避免鎖定值類型

      這是因為 Monitor.Enter方法的參數(shù)為Object類型,這就導(dǎo)致如果傳遞值類型會導(dǎo)致值類型被裝箱,進(jìn)而導(dǎo)致線程在已裝箱的對象上獲取鎖,最終線程每次調(diào)用Monitor.Enter方法都在一個完全不同的對象上獲取鎖,導(dǎo)致鎖失效,無法實現(xiàn)線程同步。

      看看下面這個代碼示例:

      public class LockValueTypeExample
      {
          private static readonly int _lock = 88;
          public void Method1()
          {
              try
              {
                  Monitor.Enter(_lock);
                  var threadId = Thread.CurrentThread.ManagedThreadId;
                  Console.WriteLine($"線程 {threadId} 通過 lock(值類型) 鎖進(jìn)入 Method1");
                  Console.WriteLine($"進(jìn)入時間 {DateTime.Now:HH:mm:ss}");
                  Console.WriteLine($"開始休眠 5 秒");
                  Console.WriteLine($"------------------------------------");
                  Thread.Sleep(5000);
              }
              finally
              {
                  Console.WriteLine($"開始釋放鎖 {DateTime.Now:HH:mm:ss}");
                  Monitor.Exit(_lock);
                  Console.WriteLine($"完成鎖釋放 {DateTime.Now:HH:mm:ss}");
              }
          }
      }
      public static void LockValueTypeRun()
      {
          var example = new LockValueTypeExample();
          var thread1 = new Thread(example.Method1);
          thread1.Start();
      }
      

      看看執(zhí)行結(jié)果:

      可以發(fā)現(xiàn)在釋放鎖的時候拋出異常,大致意思是:“對象同步方法在未同步的代碼塊中被調(diào)用。”,這就是因為鎖定的地方和釋放的地方鎖已經(jīng)不一樣了。

      02、小心try/finally

      如上面的例子,Monitor.Enter方法是寫在try塊中,試想一下:如果在Monitor.Enter方法之前拋出了異常會怎樣異常?看下面這段代碼:

      public class LockBeforeExceptionExample
      {
          private static readonly object _lock = new object();
          public void Method1()
          {
              try
              {
                  if (new Random().Next(2) == 1)
                  {
                      Console.WriteLine($"在調(diào)用Monitor.Enter前發(fā)生異常");
                      throw new Exception("在調(diào)用Monitor.Enter前發(fā)生異常");
                  }
                  Monitor.Enter(_lock);
              }
              catch (Exception ex)
              {
                  Console.WriteLine($"捕捉到異常:{ex.Message}");
              }
              finally
              {
                  Console.WriteLine($"開始釋放鎖 {DateTime.Now:HH:mm:ss}");
                  Monitor.Exit(_lock);
                  Console.WriteLine($"完成鎖釋放 {DateTime.Now:HH:mm:ss}");
              }
          }
      }
      public static void LockBeforeExceptionRun()
      {
          var example = new LockBeforeExceptionExample();
          var thread1 = new Thread(example.Method1);
          thread1.Start();
      }
      

      上面代碼是在調(diào)用Monitor.Enter方法前隨機(jī)拋出異常,當(dāng)發(fā)生異常后,可以在釋放鎖的時候和鎖定值類型報了同樣的錯誤,執(zhí)行結(jié)果如下:

      這是因為還沒有執(zhí)行鎖定就拋出異常,導(dǎo)致釋放一個沒有鎖定的鎖。

      那要如何解決這個問題呢?Monitor類已經(jīng)考慮到了這種情況,并給出了解決辦法——使用Monitor.Enter的第二個參數(shù)lockTaken,當(dāng)獲取鎖定成功則更改lockTaken為true。如此在finally的時候只需要判斷l(xiāng)ockTaken即可決定是否需要執(zhí)行釋放鎖操作,具體代碼如下:

      public class LockSolveBeforeExceptionExample
      {
          private static readonly object _lock = new object();
          public void Method1()
          {
              var lockTaken = false;
              try
              {
                  if (new Random().Next(2) == 1)
                  {
                      Console.WriteLine($"在調(diào)用Monitor.Enter前發(fā)生異常");
                      throw new Exception("在調(diào)用Monitor.Enter前發(fā)生異常");
                  }
                  Monitor.Enter(_lock,ref lockTaken);
              }
              catch (Exception ex)
              {
                  Console.WriteLine($"捕捉到異常:{ex.Message}");
              }
              finally
              {
                  if (lockTaken)
                  {
                      Console.WriteLine($"開始釋放鎖 {DateTime.Now:HH:mm:ss}");
                      Monitor.Exit(_lock);
                      Console.WriteLine($"完成鎖釋放 {DateTime.Now:HH:mm:ss}");
                  }
                  else
                  {
                      Console.WriteLine($"未執(zhí)行鎖定,無需釋放鎖");
                  }
              }
          }
      }
      public static void LockSolveBeforeExceptionRun()
      {
          var example = new LockSolveBeforeExceptionExample();
          var thread1 = new Thread(example.Method1);
          thread1.Start();
      }
      

      執(zhí)行結(jié)果如下:

      03、善用TryEnter

      我們知道使用鎖應(yīng)當(dāng)避免長時間持有鎖,長時間持有鎖會阻塞其他線程,影響性能。我們可以通過Monitor.TryEnter指定超時時間,可以看看下面示例代碼:

      public class LockTryEnterExample
      {
          private static readonly object _lock = new object();
          public void Method1()
          {
              try
              {
                  Monitor.Enter(_lock);
                  Console.WriteLine($"Method1 | 獲取鎖成功,并鎖定 5 秒");
                  Thread.Sleep(5000);
              }
              finally
              {
                  Monitor.Exit(_lock);
              }
          }
          public void Method2()
          {
              Console.WriteLine($"Method2 | 嘗試獲取鎖");
              if (Monitor.TryEnter(_lock, 3000))
              {
                  try
                  {
                  }
                  finally
                  {
                  }
              }
              else
              {
                  Console.WriteLine($"Method2 | 3 秒內(nèi)未獲取到鎖,自動退出鎖");
              }
          }
          public void Method3()
          {
              Console.WriteLine($"Method3 | 嘗試獲取鎖");
              if (Monitor.TryEnter(_lock, 7000))
              {
                  try
                  {
                      Console.WriteLine($"Method3 | 7 秒內(nèi)獲取到鎖");
                  }
                  finally
                  {
                      Console.WriteLine($"Method3 |開始釋放鎖");
                      Monitor.Exit(_lock);
                      Console.WriteLine($"Method3 |完成鎖釋放");
                  }
              }
              else
              {
                  Console.WriteLine($"Method3 | 7 秒內(nèi)未獲取到鎖,自動退出鎖");
              }
          }
      }
      public static void LockTryEnterRun()
      {
          var example = new LockTryEnterExample();
          var thread1 = new Thread(example.Method1);
          var thread2 = new Thread(example.Method2);
          var thread3 = new Thread(example.Method3);
          thread1.Start();
          thread2.Start();
          thread3.Start();
      }
      

      執(zhí)行結(jié)果如下:

      可以發(fā)現(xiàn)當(dāng)Method1鎖定5秒后,Method2嘗試3秒內(nèi)獲取鎖,結(jié)果并未獲取到自動退出;然后Method3嘗試7秒內(nèi)獲取鎖,結(jié)果獲取到鎖并正確釋放鎖。

      04、實現(xiàn)生產(chǎn)者-消費(fèi)者模式

      除了上面介紹的方法,Monitor類還有Wait、Pulse、PulseAll等方法。

      Wait: 該方法用于將當(dāng)前線程放入等待隊列,直到收到其他線程的信號通知。

      Pulse: 該方法用于喚醒等待隊列中的一個線程。當(dāng)一個線程調(diào)用 Pulse 時,它會通知一個正在等待該對象鎖的線程繼續(xù)執(zhí)行。

      PulseAll: 該方法用于喚醒等待隊列中的所有線程。

      然后我們利用Monitor類的這些功能來實現(xiàn)一個簡單的生產(chǎn)者-消費(fèi)者模式。大致思路如下:

      1.首先啟動生產(chǎn)者線程,獲取鎖,然后生成數(shù)據(jù);

      2.當(dāng)生產(chǎn)者生產(chǎn)的數(shù)據(jù)小于數(shù)據(jù)隊列長度,則生產(chǎn)一條數(shù)據(jù)同時通知消費(fèi)者線程進(jìn)行消費(fèi),否則暫停當(dāng)前線程等待消費(fèi)者線程消費(fèi)數(shù)據(jù);

      3.然后啟動消費(fèi)者線程,獲取鎖,然后消費(fèi)數(shù)據(jù);

      4.當(dāng)數(shù)據(jù)隊列中有數(shù)據(jù),則消費(fèi)一條數(shù)據(jù)同時通知生產(chǎn)者線程可以生產(chǎn)數(shù)據(jù)了,否則暫停當(dāng)前線程等待生產(chǎn)者線程生產(chǎn)數(shù)據(jù);

      具體代碼如下:

      public class LockProducerConsumerExample
      {
          private static Queue<int> queue = new Queue<int>();
          private static object _lock = new object();
          //生產(chǎn)者
          public  void Producer()
          {
              while (true)
              {
                  lock (_lock)
                  {
                      Console.ForegroundColor = ConsoleColor.Red;
                      if (queue.Count < 3)
                      {
                          var item = new Random().Next(100);
                          queue.Enqueue(item);
                          Console.WriteLine($"生產(chǎn)者,生產(chǎn): {item}");
                          //喚醒消費(fèi)者
                          Monitor.Pulse(_lock);  
                      }
                      else
                      {
                          //隊列滿時,生產(chǎn)者等待
                          Console.WriteLine($"隊列已滿,生產(chǎn)者等待中……");
                          Monitor.Wait(_lock);  
                      }
                  }
                  Thread.Sleep(500);
              }
          }
          // 消費(fèi)者
          public  void Consumer()
          {
              while (true)
              {
                  lock (_lock)
                  {
                      Console.ForegroundColor = ConsoleColor.Blue;
                      if (queue.Count > 0)
                      {
                          var item = queue.Dequeue();
                          Console.WriteLine($"消費(fèi)者,消費(fèi): {item}");
                          //喚醒生產(chǎn)者
                          Monitor.Pulse(_lock);  
                      }
                      else
                      {
                          //隊列空時,消費(fèi)者等待
                          Console.WriteLine($"隊列已空,消費(fèi)者等待中……");
                          Monitor.Wait(_lock);  
                      }
                  }
                  Thread.Sleep(10000);
              }
          }
      }
      public static void LockProducerConsumerRun()
      {
          var example = new LockProducerConsumerExample();
          var thread1 = new Thread(example.Producer);
          var thread2 = new Thread(example.Consumer);
          thread1.Start();
          thread2.Start();
          thread1.Join();
          thread2.Join();
      }
      

      執(zhí)行結(jié)果如下:

      :測試方法代碼以及示例源碼都已經(jīng)上傳至代碼庫,有興趣的可以看看。https://gitee.com/hugogoos/Planner

      posted @ 2025-02-13 15:54  IT規(guī)劃師  閱讀(887)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 湖南省| 安仁县| 精品国产成人午夜福利| 精品无码久久久久久尤物| 中文午夜乱理片无码| 欧美国产日韩久久mv| 亚洲欧美日韩成人综合一区| 狠狠躁夜夜躁人人爽天天古典| 中文国产成人久久精品小说| 久久久久青草线蕉综合超碰| a男人的天堂久久a毛片| 在线免费观看毛片av| b站永久免费看片大全| 精品国产一区二区三区蜜臀| 一区二区中文字幕av| 久青草国产在视频在线观看| 日本高清一区免费中文视频| 美女内射无套日韩免费播放| 91孕妇精品一区二区三区| 亚洲区一区二区三区亚洲| 国产精品久久久久久无毒不卡| 亚洲精品成人福利网站| 99国产午夜福利在线观看| 成码无人AV片在线电影网站| 欧美日韩国产码高清| 无码高潮爽到爆的喷水视频app| 国产午夜精品福利视频| 鱼台县| 精品视频国产狼友视频| 人人妻人人狠人人爽天天综合网| 日韩精品一区二区三区激情视频| 亚洲欧美另类久久久精品播放的| av一区二区中文字幕| 亚洲国产中文字幕精品| 国产成人一区二区不卡| 国产精品国产三级国产专i| 偷拍精品一区二区三区| 成在线人视频免费视频| 久久99精品国产99久久6男男| 极品一区二区三区水蜜桃| 国产午夜精品福利视频|