聊透多線程編程-線程互斥與同步-12. C# Monitor類實現線程互斥

目錄

一、什么是臨界區?

二、Monitor類的用途

三、Monitor的基本用法

四、Monitor的工作原理

五、使用示例1-保護共享變量

解釋:

六、使用示例2-線程間信號傳遞

解釋:

七、注意事項

八、總結


?

在多線程編程中,線程之間的同步和互斥是確保程序正確運行的關鍵。C# 提供了多種機制來實現線程同步,其中 Monitor 類是一個底層但功能強大的工具,用于實現線程間的互斥訪問。本文將詳細介紹如何使用 Monitor 類實現線程互斥,并通過示例展示其工作原理。


一、什么是臨界區?

在多線程編程中,臨界區是指一段需要互斥訪問的代碼塊,通常涉及對共享資源的操作。為了避免多個線程同時操作共享資源而導致數據競爭或狀態不一致,我們需要對臨界區代碼進行保護。

例如,如果兩個線程同時修改一個共享變量,可能會導致最終結果不符合預期。因此,我們需要一種機制來確保同一時間只有一個線程可以進入臨界區。


二、Monitor類的簡介

Monitor 類是 .NET 提供的一個低級線程同步工具,主要用于實現線程間的互斥訪問(Monitor 實現的線程互斥主要是一種軟件實現方法,但并不是基于經典的線程同步算法如單標志法、雙標志法或 Peterson 方法來實現的,而是基于現代操作系統和硬件提供的更高級別的同步原語(如原子操作和內核對象)構建的,這是因為單標志法、雙標志法和 Peterson 方法都依賴于輪詢(busy-waiting),這會導致 CPU 資源浪費,不適合現代多處理器環境)。與 lock 關鍵字不同,Monitor 提供了更細粒度的控制能力,允許開發者手動管理鎖的獲取和釋放。

Monitor 的主要方法包括:

  • Monitor.Enter(object):嘗試獲取指定對象的鎖。如果鎖已被占用,則當前線程會被阻塞,直到鎖被釋放。
  • Monitor.TryEnter(object):嘗試獲取指定對象的鎖,但不會無限期阻塞。它返回一個布爾值,指示是否成功獲取鎖。可以通過重載版本指定超時時間(以毫秒為單位),如果在指定時間內未能獲取鎖,則返回?false。
  • Monitor.Exit(object):釋放指定對象的鎖。
  • Monitor.Wait(object):釋放鎖并使當前線程進入等待狀態,直到其他線程調用?Monitor.Pulse?或?Monitor.PulseAll。
  • Monitor.Pulse(object):通知等待隊列中的一個線程繼續執行。
  • Monitor.PulseAll(object):通知等待隊列中的所有線程繼續執行。

三、Monitor的基本用法

Monitor 的基本用法類似于 lock,但需要手動調用 EnterExit 方法。以下是一個簡單的例子:

private static readonly object _lock = new object(); // 鎖對象Monitor.Enter(_lock);
try
{// 需要同步的代碼塊
}
finally
{Monitor.Exit(_lock); // 確保鎖一定會被釋放
}
  • _lock?是一個引用類型的對象,作為鎖的標識。
  • 使用?try-finally?塊是為了確保即使發生異常,鎖也能被正確釋放。

四、Monitor的工作原理

Monitor 類的核心思想是基于鎖對象的互斥機制:

  1. 當線程調用?Monitor.Enter(object)?時,它會嘗試獲取指定對象的鎖。如果鎖已被其他線程占用,則當前線程會被掛起,直到鎖可用。
  2. 當線程調用?Monitor.Exit(object)?時,它會釋放鎖,允許其他線程獲取該鎖。
  3. Monitor.Wait?和?Monitor.Pulse?則用于實現線程間的信號傳遞,允許線程在特定條件下暫停或恢復執行。

五、使用示例1-保護共享變量

下面是一個使用 Monitor 類保護共享變量的例子:

using System;
using System.Threading;class Program
{private static int _counter = 0;private static readonly object _lock = new object();static void Main(){Thread t1 = new Thread(IncrementCounter);Thread t2 = new Thread(IncrementCounter);t1.Start();t2.Start();t1.Join();t2.Join();Console.WriteLine($"Final Counter Value: {_counter}");}static void IncrementCounter(){for (int i = 0; i < 100000; i++){Monitor.Enter(_lock);try{_counter++;}finally{Monitor.Exit(_lock);}}}
}

解釋:

  • _lock?是一個靜態對象,用于標識鎖。
  • 每次訪問?_counter?時,都會通過?Monitor.Enter?獲取鎖,并通過?Monitor.Exit?釋放鎖。
  • 最終輸出的結果是?200000,因為所有線程的操作都被正確同步了。

六、使用示例2-線程間信號傳遞

Monitor 類還可以用于實現線程間的信號傳遞。以下是一個典型的生產者-消費者模型示例:

using System;
using System.Threading;class Program
{private static readonly object _lock = new object();private static bool _isReady = false; // 共享的狀態變量static void Main(string[] args){Thread threadA = new Thread(DoWorkA);Thread threadB = new Thread(DoWorkB);threadA.Start();threadB.Start();threadA.Join();threadB.Join();}static void DoWorkA(){lock (_lock){Console.WriteLine("Thread A: Waiting for signal...");// 等待條件滿足while (!_isReady){Monitor.Wait(_lock); // 釋放鎖并等待}Console.WriteLine("Thread A: Received signal. Continuing work.");}}static void DoWorkB(){Thread.Sleep(2000); // 模擬一些工作lock (_lock){Console.WriteLine("Thread B: Preparing to signal...");// 設置條件為 true_isReady = true;// 通知等待的線程Monitor.Pulse(_lock);Console.WriteLine("Thread B: Signal sent.");}}
}

解釋:

  • 線程 A 調用?Monitor.Wait?釋放鎖并進入等待狀態,直到線程 B 調用?Monitor.Pulse?通知它繼續執行。
  • 這種機制非常適合需要線程間協作的場景。

七、注意事項

  1. 鎖對象的選擇

    • 鎖對象必須是引用類型(如?object),不能是值類型(如?int)。
    • 推薦使用專用的私有對象作為鎖對象,避免與其他代碼發生沖突。
    • 不要使用?this?或字符串常量作為鎖對象,以免引發死鎖。
  2. 避免死鎖

    • 死鎖是指多個線程互相等待對方釋放鎖,導致程序無法繼續運行。
    • 避免死鎖的方法包括:
      • 確保鎖的獲取順序一致。
      • 盡量減少鎖的范圍。
  3. 性能影響

    • 使用?Monitor?會導致線程阻塞,從而影響性能。
    • 對于高并發場景,可以考慮使用其他同步機制(如?SemaphoreSlim?或?ReaderWriterLockSlim)。

八、總結

Monitor 類是 C# 中實現線程互斥的一種重要工具,提供了比 lock 更靈活的控制能力。盡管它的使用稍微復雜一些,但能夠滿足更多高級需求,例如線程間的信號傳遞。

在實際開發中,選擇合適的同步機制非常重要。對于簡單的線程互斥場景,lock 可能更為直觀;而對于需要更細粒度控制的場景,Monitor 則是一個不錯的選擇。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/78434.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78434.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/78434.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

第R4周:LSTM-火災溫度預測

文章目錄 一、前期準備工作1.導入數據2. 數據集可視化 二、構建數據集1. 數據集預處理2. 設置X, y3. 劃分數據集 三、模型訓練1. 構建模型2. 定義訓練函數3. 定義測試函數4. 正式訓練模型 四、模型評估1. Loss圖片2. 調用模型進行預測3. R2值評估 總結&#xff1a; &#x1f36…

toCharArray作用

toCharArray() 是 Java 中 String 類的一個方法&#xff0c;其作用是將字符串對象轉換為一個字符數組。下面為你詳細介紹其用法、原理和示例。 方法定義 toCharArray() 方法在 java.lang.String 類里被定義&#xff0c;方法簽名如下 public char[] toCharArray() 此方法沒有…

STM32八股【6】-----CortexM3的雙堆棧(MSP、PSP)設計

STM32的線程模式&#xff08;Thread Mode&#xff09;和內核模式&#xff08;Handler Mode&#xff09;以及其對應的權級和堆棧指針 線程模式&#xff1a; 正常代碼執行時的模式&#xff08;如 main 函數、FreeRTOS任務&#xff09; 可以是特權級&#xff08;使用MSP&#xff…

驅動支持的最高CUDA版本與實際安裝的Runtime版本

查看電腦上安裝的CUDA版本的多種方法&#xff0c;適用于不同系統和場景。 方法一&#xff1a;通過命令行工具 1. 查看CUDA Driver API版本&#xff08;顯卡驅動支持的CUDA版本&#xff09; 命令&#xff1a;nvidia-smi操作&#xff1a; 打開終端&#xff08;Windows為CMD/Pow…

Python CT圖像預處理——基于ITK-SNAP

Python CT圖像預處理——nii格式讀取、重采樣、窗寬窗位設置_python讀取nii-CSDN博客 基于原文指出以下幾個問題&#xff1a;文件路徑設置模糊&#xff1b;nilabel里面使用的get_data() 方法已經過時&#xff1b;需要導入scikit-image&#xff0c;還要導入一個matplotlib。 一…

【MQ篇】RabbitMQ之消息持久化!

目錄 一、 交換機持久化 (Exchange Persistence)二、 隊列持久化 (Queue Persistence)三、 消息持久化 (Message Persistence)四、 持久化的“黃金三角” &#x1f531;&#xff1a;三者缺一不可&#xff01;五、 來&#xff0c;完整的代碼示例&#xff08;整合持久化和確認機制…

[AI技術(二)]JSONRPC協議MCPRAGAgent

Agent概述(一) AI技術基礎(一) JSON-RPC 2.0 協議詳解 JSON-RPC 2.0 是一種基于 JSON 的輕量級遠程過程調用(RPC)協議,旨在簡化跨語言、跨平臺的遠程通信。以下從協議特性、核心結構、錯誤處理、批量請求等角度進行詳細解析: 一、協議概述 1. 設計原則 ? 簡單性:…

LeetCode238_除自身以外數組的乘積

LeetCode238_除自身以外數組的乘積 標簽&#xff1a;#數組 #前綴和Ⅰ. 題目Ⅱ. 示例0. 個人方法一&#xff1a;暴力循環嵌套0. 個人方法二&#xff1a;前綴和后綴分別求積 標簽&#xff1a;#數組 #前綴和 Ⅰ. 題目 給你一個整數數組 nums&#xff0c;返回 數組 answer &#…

算法筆記.spfa算法(bellman-ford算法的改進)

題目&#xff1a;&#xff08;來源于AcWing&#xff09; 給定一個 n 個點 m 條邊的有向圖&#xff0c;圖中可能存在重邊和自環&#xff0c; 邊權可能為負數。 請你求出 1 號點到 n 號點的最短距離&#xff0c;如果無法從 1 號點走到 n 號點&#xff0c;則輸出 impossible。 …

07 Python 字符串全解析

文章目錄 一. 字符串的定義二. 字符串的基本用法1. 訪問字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重復5.字符串比較6.字符串成員運算 三. 字符串的常用方法1. len() 函數2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的進階…

Java集成Zxing和OpenCV實現二維碼生成與識別工具類

Java集成Zxing和OpenCV實現二維碼生成與識別工具類 本文將介紹如何使用Java集成Zxing和OpenCV庫&#xff0c;實現二維碼的生成和識別功能。識別方法支持多種輸入形式&#xff0c;包括File對象、文件路徑和Base64編碼。 一、環境準備 添加Maven依賴 <dependencies><…

【專題刷題】二分查找(二)

&#x1f4dd;前言說明&#xff1a; 本專欄主要記錄本人的基礎算法學習以及LeetCode刷題記錄&#xff0c;按專題劃分每題主要記錄&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代碼&#xff1b;&#xff08;2&#xff09;優質解法 優質代碼&#xff1b;&#xff…

Java—ThreadLocal底層實現原理

首先&#xff0c;ThreadLocal 本身并不提供存儲數據的功能&#xff0c;當我們操作 ThreadLocal 的時候&#xff0c;實際上操作線程對象的一個名為 threadLocals 成員變量。這個成員變量的類型是 ThreadLocal 的一個內部類 ThreadLocalMap&#xff0c;它是真正用來存儲數據的容器…

Elasticsearch(ES)中的腳本(Script)

文章目錄 一. 腳本是什么&#xff1f;1. lang&#xff08;腳本語言&#xff09;2. source&#xff08;腳本代碼&#xff09;3. params&#xff08;參數&#xff09;4. id&#xff08;存儲腳本的標識符&#xff09;5. stored&#xff08;是否為存儲腳本&#xff09;6. script 的…

客戶聯絡中心能力與客戶匹配方式

在數字化時代&#xff0c;客戶聯絡中心作為企業與客戶溝通的核心樞紐&#xff0c;其服務能力與客戶需求的精準匹配至關重要。隨著客戶期望的不斷提升&#xff0c;傳統的“一刀切”服務模式已難以滿足個性化需求&#xff0c;如何通過智能化的手段實現服務能力與客戶的高效匹配&a…

深入理解網絡原理:UDP協議詳解

在計算機網絡中&#xff0c;數據的傳輸是通過各種協議實現的&#xff0c;其中用戶數據報協議&#xff08;UDP&#xff0c;User Datagram Protocol&#xff09;作為一種重要的傳輸層協議&#xff0c;廣泛應用于實時通信、視頻流、在線游戲等場景。本文將深入探討UDP協議的特性、…

vscode切換Python環境

跑深度學習項目通常需要切換python環境&#xff0c;下面介紹如何在vscode切換python環境&#xff1a; 1.點擊vscode界面左上角 2.在彈出框選擇對應kernel

【MCP Node.js SDK 全棧進階指南】中級篇(4):MCP錯誤處理與日志系統

前言 隨著MCP應用的規模和復雜性增長,錯誤處理與日志系統的重要性也日益凸顯。一個健壯的錯誤處理策略和高效的日志系統不僅可以幫助開發者快速定位和解決問題,還能提高應用的可靠性和可維護性。本文作為中級篇的第四篇,將深入探討MCP TypeScript-SDK中的錯誤處理與日志系統…

【Qt】文件

&#x1f308; 個人主頁&#xff1a;Zfox_ &#x1f525; 系列專欄&#xff1a;Qt 目錄 一&#xff1a;&#x1f525; Qt 文件概述 二&#xff1a;&#x1f525; 輸入輸出設備類 三&#xff1a;&#x1f525; 文件讀寫類 四&#xff1a;&#x1f525; 文件和目錄信息類 五&…

代碼隨想錄算法訓練營第五十八天 | 1.拓撲排序精講 2.dijkstra(樸素版)精講 卡碼網117.網站構建 卡碼網47.參加科學大會

1.拓撲排序精講 題目鏈接&#xff1a;117. 軟件構建 文章講解&#xff1a;代碼隨想錄 思路&#xff1a; 把有向無環圖進行線性排序的算法都可以叫做拓撲排序。 實現拓撲排序的算法有兩種&#xff1a;卡恩算法&#xff08;BFS&#xff09;和DFS&#xff0c;以下BFS的實現思…