.net core 線程鎖,互斥鎖,自旋鎖,混合鎖

線程鎖、互斥鎖、自旋鎖和混合鎖是多線程編程中的重要概念,它們用于控制對共享資源的訪問,避免數據競爭和不一致性。每種鎖有其特定的適用場景和特點。我們來逐一解釋它們,并進行比較。

1. 線程鎖(Thread Lock)

線程鎖的概念泛指任何用于同步多線程訪問共享資源的機制。它的目的是確保在同一時刻只有一個線程可以訪問資源,從而避免多個線程并發訪問時發生數據競爭(race condition)或資源不一致。

線程鎖通常是通過以下幾種鎖機制來實現的:

  • 互斥鎖(Mutex)
  • 自旋鎖(SpinLock)
  • 讀寫鎖(ReadWriteLock)
  • 信號量(Semaphore)
  • 臨界區(CriticalSection)

不同類型的鎖有不同的實現方式和適用場景。

2. 互斥鎖(Mutex)

互斥鎖(Mutex)是一種最常見的同步原語,用于控制對共享資源的訪問。它的基本思想是:如果一個線程已經獲得了鎖,其他線程必須等待,直到鎖被釋放,才能繼續執行。

特點
  • 線程阻塞:當一個線程嘗試獲取互斥鎖時,如果鎖已被其他線程持有,線程會被掛起,直到鎖可用為止。
  • 適用于長時間持有鎖的情況:如果臨界區代碼較長,或線程會執行大量計算時,使用互斥鎖能有效避免 CPU 資源的浪費。
  • 系統開銷較高:掛起和恢復線程的操作比自旋等待更消耗系統資源。
示例:C# 中的?lock(實際上是基于?Monitor?的實現)
class Program
{private static readonly object lockObj = new object();private static int counter = 0;static void Main(){Thread thread1 = new Thread(IncrementCounter);Thread thread2 = new Thread(IncrementCounter);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine("Final counter value: " + counter);}static void IncrementCounter(){lock (lockObj)  // 獲取鎖{counter++;  // 臨界區Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} incremented counter to {counter}");}}
}

3. 自旋鎖(SpinLock)

自旋鎖是一種非常輕量級的同步機制,線程在嘗試獲取鎖時,不會被掛起,而是會在一個循環中不斷檢查鎖是否已經釋放。線程會不斷“自旋”并消耗 CPU 時間,直到獲得鎖。

特點
  • 忙等待:當一個線程請求自旋鎖時,如果鎖已經被其他線程持有,它會不斷地檢查鎖是否已被釋放,這種行為被稱為“自旋”。
  • 適用于鎖持有時間短的場景:當臨界區代碼執行時間非常短時,自旋鎖可以避免線程掛起和恢復的高開銷。
  • CPU 資源消耗較高:如果鎖持有時間較長,多個線程可能會造成大量 CPU 資源的浪費。
示例:C# 中的?SpinLock
using System;
using System.Threading;class Program
{private static SpinLock spinLock = new SpinLock();private static int counter = 0;static void Main(){Thread thread1 = new Thread(IncrementCounter);Thread thread2 = new Thread(IncrementCounter);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine("Final counter value: " + counter);}static void IncrementCounter(){bool lockTaken = false;try{spinLock.Enter(ref lockTaken);  // 獲取鎖counter++;  // 臨界區Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} incremented counter to {counter}");}finally{if (lockTaken)spinLock.Exit();  // 釋放鎖}}
}

4. 混合鎖(Hybrid Lock)

混合鎖是一種結合了互斥鎖和自旋鎖的鎖機制,它通常用于試圖在自旋鎖和互斥鎖之間根據具體情況進行切換,旨在提高多線程程序的效率。

混合鎖的思想是:

  1. 自旋鎖:在鎖爭用輕微、臨界區代碼執行時間短的情況下,使用自旋鎖來減少線程掛起帶來的性能開銷。
  2. 互斥鎖:如果自旋鎖的時間過長,系統會自動切換為互斥鎖,這樣線程會被掛起,避免浪費過多 CPU 時間。
特點
  • 適應性強:混合鎖通過平衡自旋和線程掛起的開銷,避免在鎖爭用過于嚴重時造成資源浪費。
  • 自動調整:當爭用變得嚴重時,混合鎖會自動切換為互斥鎖,而在爭用輕微時,它會使用自旋來避免不必要的開銷。
示例:C# 中沒有直接的混合鎖類,但可以通過自定義邏輯來實現類似功能。
using System;
using System.Threading;class Program
{private static SpinLock spinLock = new SpinLock();private static object mutex = new object();private static int counter = 0;static void Main(){Thread thread1 = new Thread(IncrementCounter);Thread thread2 = new Thread(IncrementCounter);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine("Final counter value: " + counter);}static void IncrementCounter(){bool lockTaken = false;try{// 嘗試自旋鎖if (!spinLock.TryEnter(100))  // 如果鎖在 100ms 內未被獲取{// 自旋失敗,使用互斥鎖lock (mutex){counter++;Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} incremented counter to {counter}");}}else{// 獲取自旋鎖counter++;Console.WriteLine($"Thread {Thread.CurrentThread.ManagedThreadId} incremented counter to {counter}");}}finally{if (lockTaken)spinLock.Exit();}}
}

自旋鎖、互斥鎖和混合鎖的比較

特性/鎖類型互斥鎖(Mutex)自旋鎖(SpinLock)混合鎖(Hybrid Lock)
鎖獲取方式阻塞,線程被掛起自旋,線程忙等待鎖根據鎖的爭用情況自旋或阻塞
適用場景鎖持有時間長、鎖競爭激烈的情況鎖持有時間短、鎖競爭輕的情況鎖持有時間變化,既有自旋又有阻塞
性能開銷較高,線程掛起與恢復開銷較大較低,但如果競爭嚴重會浪費 CPU 資源較低,可以根據情況自動調整
適用性多線程競爭較高的場景低競爭、鎖持有時間短的場景高競爭情況下動態選擇鎖類型
總結
  • 互斥鎖?適用于鎖持有時間較長、競爭激烈的場景,能有效避免資源爭用,但可能會導致性能瓶頸。
  • 自旋鎖?適用于鎖持有時間非常短的場景,能夠避免線程上下文切換的開銷,但如果鎖爭用嚴重,可能會浪費大量 CPU 資源。
  • 混合鎖?結合了自旋鎖和互斥鎖的優點,能根據鎖爭用情況動態選擇自旋或掛起,從而提供更好的性能和適應性。

選擇哪種鎖取決于具體的應用場景和性能需求。在高并發、高競爭的環境中,混合鎖可能是最優選擇,而在低競爭或快速臨界區的情況下,自旋鎖也許是最合適的。

5.信號量

信號量(Semaphore) 是一種用于多線程編程中的同步機制,用于控制對共享資源的訪問,特別是在資源數量有限時,它能夠限制并發訪問的線程數目。信號量通過維護一個計數器來管理線程的訪問。線程在進入臨界區之前,需要檢查信號量的計數值,只有計數值大于零時,線程才能進入;當線程完成工作后,信號量的計數值會增加,允許其他線程進入。

信號量的基本概念
  • 計數器:信號量內部有一個整數計數器,表示可用的資源數量或允許并發執行的線程數。
  • P操作(或稱為?Wait?或?Acquire):線程嘗試減少信號量的計數器。如果信號量的計數器大于零,線程會成功進入臨界區,計數器減一。如果計數器為零,線程會被阻塞,直到計數器大于零。
  • V操作(或稱為?Signal?或?Release):線程在完成工作后,增加信號量的計數器,允許其他被阻塞的線程繼續執行。
信號量的類型
  1. 計數信號量(Counting Semaphore):計數信號量的計數器值可以是任意非負整數,表示允許訪問的資源數量或線程數。例如,如果有 5 個資源或 5 個線程可以并發執行,信號量的初始值為 5。每當一個線程獲得資源時,計數器減一,釋放資源時計數器加一。

  2. 二值信號量(Binary Semaphore):二值信號量是計數信號量的一種特殊情況,計數器值僅為 0 或 1。它常常用于控制一個線程的互斥訪問,類似于互斥鎖(Mutex)。二值信號量也被稱為 互斥信號量,因為它的行為與互斥鎖非常相似。

信號量的應用場景
  • 控制并發訪問:信號量通常用于控制某些資源的并發訪問,限制同時訪問某些共享資源的線程數。例如,數據庫連接池中的數據庫連接數有限,信號量可以用來確保不超過最大連接數。

  • 限制資源數量:例如,線程池中只允許一定數量的線程同時運行任務,超出限制的線程會被阻塞,直到其他線程完成任務并釋放資源。

  • 線程同步:在一些需要線程同步的場景中,信號量可以用來控制線程的執行順序或協調多個線程之間的操作。

示例:C# 中使用信號量

假設我們有一個共享的數據庫連接池,最多只允許 3 個線程同時訪問數據庫。我們可以使用信號量來限制并發訪問。

using System;
using System.Threading;class Program
{// 初始化信號量,最多允許 3 個線程并發訪問private static Semaphore semaphore = new Semaphore(3, 3); static void Main(){// 創建并啟動 5 個線程for (int i = 0; i < 5; i++){int threadId = i;Thread thread = new Thread(() => AccessDatabase(threadId));thread.Start();}}static void AccessDatabase(int threadId){Console.WriteLine($"Thread {threadId} trying to access database...");// 嘗試獲取信號量semaphore.WaitOne();  // 如果信號量計數器大于 0,則進入臨界區,計數器減 1try{Console.WriteLine($"Thread {threadId} is accessing the database.");Thread.Sleep(2000);  // 模擬數據庫訪問操作Console.WriteLine($"Thread {threadId} is done with the database.");}finally{// 釋放信號量semaphore.Release();  // 釋放資源,信號量計數器加 1}}
}
代碼解釋
  1. 信號量初始化:我們使用 Semaphore(3, 3) 來創建一個信號量,初始值為 3,表示最多允許 3 個線程同時訪問共享資源(這里是模擬的數據庫連接)。信號量的最大值也是 3,意味著最多只能有 3 個線程持有信號量。

  2. 線程嘗試訪問資源:每個線程在訪問數據庫之前調用 semaphore.WaitOne() 來嘗試獲取信號量。如果信號量的計數器大于 0,線程就能成功獲得信號量并進入臨界區,計數器減 1;如果計數器為 0,線程會被阻塞,直到其他線程釋放信號量。

  3. 線程完成后釋放信號量:在 finally 塊中,線程完成工作后調用 semaphore.Release() 來釋放信號量,允許其他線程訪問共享資源。此時,信號量計數器加 1。

信號量與其他同步機制的比較
特性/機制信號量(Semaphore)互斥鎖(Mutex)讀寫鎖(ReadWriteLock)自旋鎖(SpinLock)
鎖粒度用于控制資源數量用于單個資源的互斥訪問分別對讀和寫操作加鎖輕量級的鎖,用于短時間臨界區
適用場景控制資源數量,限流,多線程并發訪問防止多線程同時訪問共享資源允許多個讀者同時訪問,寫者互斥高并發且鎖持有時間短的場景
阻塞方式阻塞線程或繼續執行阻塞線程阻塞線程自旋,直到獲得鎖
優點控制并發數量,靈活高效確保資源的獨占訪問提高讀取性能,允許并發讀取輕量級,減少上下文切換的開銷
總結

信號量是一種用于控制并發訪問共享資源的同步工具,特別適用于資源數量有限的場景。它通過計數器來控制允許訪問的線程數量,支持靈活的線程同步與調度。根據資源需求,信號量能夠控制多個線程的并發執行,避免資源爭用和沖突。

6.讀寫鎖

讀寫鎖是一種特殊類型的鎖,它允許多個線程同時讀取共享數據,但在寫操作時,只能有一個線程進行寫操作,而且在寫操作時,其他線程不能進行讀操作或寫操作。讀寫鎖旨在提高讀操作多、寫操作少的場景下的性能,尤其是在數據讀取頻繁而修改較少的情況下。

讀寫鎖的工作原理
  • 讀鎖:多個線程可以同時持有讀鎖,只要沒有線程持有寫鎖。讀鎖不會阻止其他線程獲取讀鎖。
  • 寫鎖:寫鎖是排他性的,只有一個線程可以持有寫鎖。并且在持有寫鎖時,所有其他線程(無論是讀鎖還是寫鎖)都不能訪問共享資源。
  • 讀寫鎖的基本設計思想是:在沒有寫操作的情況下,允許多個線程并發讀取;但是一旦有寫操作開始,必須保證其他線程都無法訪問資源。

C# 中的 ReaderWriterLockSlim

在 C# 中,ReaderWriterLockSlim 類提供了類似的功能,用于處理并發讀寫操作。

  • EnterReadLock():獲取讀鎖,允許多個線程并發讀取。
  • EnterWriteLock():獲取寫鎖,排他性鎖定,阻塞所有讀寫操作。
using System;
using System.Threading;class Program
{static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();static int sharedResource = 0;static void Main(){// 創建并發讀取的線程Thread readThread1 = new Thread(() =>{rwLock.EnterReadLock();  // 獲取讀鎖try{Console.WriteLine("Read Thread 1: " + sharedResource);}finally{rwLock.ExitReadLock();  // 釋放讀鎖}});Thread readThread2 = new Thread(() =>{rwLock.EnterReadLock();  // 獲取讀鎖try{Console.WriteLine("Read Thread 2: " + sharedResource);}finally{rwLock.ExitReadLock();  // 釋放讀鎖}});// 創建寫線程Thread writeThread = new Thread(() =>{rwLock.EnterWriteLock();  // 獲取寫鎖try{sharedResource++;Console.WriteLine("Write Thread: " + sharedResource);}finally{rwLock.ExitWriteLock();  // 釋放寫鎖}});// 啟動線程readThread1.Start();readThread2.Start();writeThread.Start();}
}
讀寫鎖的優勢和適用場景
優勢:
  1. 提高并發性能:當讀操作頻繁而寫操作較少時,使用讀寫鎖可以顯著提高系統的并發性能。多個線程可以同時進行讀操作,而無需等待鎖的釋放。
  2. 減少鎖競爭:由于讀操作不互斥,可以避免頻繁的鎖競爭,尤其在讀操作占主導的場景中。
  3. 提供更細粒度的控制:相比傳統的互斥鎖(如?ReentrantLock),讀寫鎖提供了更細粒度的鎖機制,讓讀寫操作更加高效。
適用場景:
  • 讀多寫少的場景:比如緩存、日志讀取、數據庫查詢等,系統中的大多數操作是讀操作,少量寫操作。
  • 高并發讀取:需要多個線程頻繁讀取共享資源,但寫操作較少的應用(例如 Web 應用中的數據查詢)。
  • 低并發寫操作:確保在寫操作發生時,不會有其他線程同時執行讀操作,保持數據一致性。
需要注意的問題:
  1. 寫操作可能會阻塞讀操作:如果有大量的讀操作而只有少數的寫操作,寫操作會造成較長時間的阻塞,導致性能下降。
  2. 死鎖風險:在設計并發系統時,如果不小心使用了寫鎖嵌套或讀鎖嵌套,可能會導致死鎖。
總結
  • 讀寫鎖的設計旨在提高系統的并發性,特別是在讀多寫少的場景下。通過區分讀鎖和寫鎖,讀寫鎖允許多個線程并行讀操作,但寫操作則是排他性的。
  • 它適用于需要大量讀取操作且寫操作相對較少的場景,可以有效減少線程之間的鎖競爭,提高系統的性能。

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

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

相關文章

【ArcGISPro/GeoScenePro】檢查并處理高程數據

數據 https://arcgis.com/sharing/rest/content/items/535efce0e3a04c8790ed7cc7ea96d02d/data 數字高程模型 (DEM) 是一種柵格,可顯示地面或地形的高程。 數字表面模型 (DSM) 是另一種高程柵格,可顯示表面的高度,例如建筑物或樹冠的頂部。 您需要準備 DEM 和 DSM 以供分析…

【C++面向對象——類與對象】Computer類(頭歌實踐教學平臺習題)【合集】

目錄&#x1f60b; 任務描述 相關知識 一、不同訪問屬性成員的訪問方式 1. public成員 2. private成員 3. protected成員 二、觀察構造函數和析構函數的執行過程 1. 構造函數 2. 析構函數 三、學習類的組合使用方法 1. 類的組合概念 2. 實現示例 實驗步驟 測試說明 …

xilinx的高速接口構成原理和連接結構及ibert工具的使用-以k7 GTX為例

一、相關簡介 Xilinx的高速接口稱之為transceivers(高速收發器&#xff09;&#xff0c;這部分的電路是專用電路&#xff0c;供電等都是獨立的&#xff0c;根據速率可以分為GTP/GTX/GTH/GTY/GTM等。 Xilinx的高速接口是QUAD為單位的&#xff0c;沒一個QUAD由一個時鐘COMMON資…

創建型模式4.原型模式

創建型模式 工廠方法模式&#xff08;Factory Method Pattern&#xff09;抽象工廠模式&#xff08;Abstract Factory Pattern&#xff09;建造者模式&#xff08;Builder Pattern&#xff09;原型模式&#xff08;Prototype Pattern&#xff09;單例模式&#xff08;Singleto…

python學opencv|讀取圖像(二十七)使用time()繪制彈球動畫

【1】引言 前序已經學習了pythonopencv畫線段、圓形、矩形、多邊形和文字的相關操作&#xff0c;具體文章鏈接包括且不限于&#xff1a; python學opencv|讀取圖像&#xff08;十八&#xff09;使用cv2.line創造線段_cv2. 畫線段-CSDN博客 python學opencv|讀取圖像&#xff0…

rabbitmq——歲月云實戰筆記

1 rabbitmq設計 生產者并不是直接將消息投遞到queue&#xff0c;而是發送給exchange&#xff0c;由exchange根據type的規則來選定投遞的queue&#xff0c;這樣消息設計在生產者和消費者就實現解耦。 rabbitmq會給沒有type預定義一些exchage&#xff0c;而實際我們卻應該使用自己…

2.系統學習-邏輯回歸

邏輯回歸 前言最大似然估計概率似然函數(likelihood function)最大似然估計 邏輯回歸邏輯回歸的似然函數與梯度 分類問題常用評價指標項目案例拓展內容作業 前言 邏輯回歸與線性回歸均屬于廣義線性模型&#xff0c;區別在于線性回歸用于解決回歸問題&#xff0c;例如身高、銷量…

記錄一次電腦被入侵用來挖礦的過程(Trojan、Miner、Hack、turminoob)

文章目錄 0、總結1、背景2、端倪3、有個微軟的系統更新&#xff0c;就想著更新看看&#xff08;能否沖掉問題&#xff09;4、更新沒成功&#xff0c;自動重啟電腦5、風險文件&#xff08;好家伙命名還挺規范&#xff0c;一看名字就知道出問題了&#xff09;6、開機有一些注冊表…

行為樹詳解(6)——黑板模式

【動作節點數據共享】 行為樹中需要的參數可以來自游戲中的各個模塊&#xff0c;如果僅需從多個模塊獲取少量參數&#xff0c;那么可以直接在代碼中調用其他模塊的單例繼而層層調用獲取數據。 如果獲取的參數量很大&#xff0c;從架構上看&#xff0c;我們需要通過加一個中間…

阿里云 人工智能與機器學習

阿里云的 人工智能&#xff08;AI&#xff09;與機器學習&#xff08;ML&#xff09; 服務為企業提供了全面的AI解決方案&#xff0c;幫助用戶在多個行業實現數據智能化&#xff0c;提升決策效率&#xff0c;推動業務創新。阿里云通過先進的技術和豐富的工具&#xff0c;支持用…

如果Adobe 退出中國后怎么辦

最近聽說Adobe要退出中國了?那咱們的設計師們可得好好想想怎么搞到正版軟件了。別急&#xff0c;今天教大家一個超酷的福利——Edu郵箱&#xff01; Edu郵箱是什么&#xff1f;有什么好處&#xff1f; Edu郵箱就是學校給學生和老師們發的郵箱&#xff0c;一般結尾是.edu。有了…

Structured-Streaming集成Kafka

一、上下文 《Structured-Streaming初識》博客中已經初步認識了Structured-Streaming&#xff0c;Kafka作為目前最流行的一個分布式的實時流消息系統&#xff0c;是眾多實時流處理框架的最優數據源之一。下面我們就跟著官方例子來看看Structured-Streaming是如何集成Kafka的&a…

Spring Boot 項目中集成 Kafka-03

在 Spring Boot 項目中集成 Kafka 有多種方式&#xff0c;適應不同的應用場景和需求。以下將詳細介紹幾種常用的集成方法&#xff0c;包括&#xff1a; 使用 Spring Kafka (KafkaTemplate 和 KafkaListener)使用 Spring Cloud Stream 與 Kafka Binder使用 Spring for Apache K…

生物醫學信號處理--緒論

前言 參考書籍&#xff1a;劉海龍&#xff0c;生物醫學信號處理&#xff0c;化學工業出版社 生物醫學信號分類 1、由生理過程自發或者誘發產生的電生理信號和非電生理信號 ? 電生理信號&#xff1a;ECG/心電、EEG/腦電、EMG/肌電、 EGG/胃電、 EOG/眼電 ? 非電生理信號&am…

unity 播放 序列幀圖片 動畫

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 前言一、方法一&#xff1a;代碼控制播放序列幀1、設置圖片屬性2、創建Image組件3、簡單的代碼控制4、掛載代碼并賦值 二、方法二&#xff1a;直接使用1.Image上添加…

QT c++ 自定義按鈕類 加載圖片 美化按鈕

如果你有需要利用圖片美化按鈕的情況&#xff0c;本文能幫助你。 鼠標左鍵按下按鈕和松開&#xff0c;按鈕顯示不同的圖片。 1.按鈕類 //因為此類比較簡單&#xff0c;1個頭文件搞定&#xff0c;沒有cpp文件 #ifndef CUSTOMBUTTON_H #define CUSTOMBUTTON_H #include <Q…

web漏洞之文件包含漏洞

一、文件包含漏洞 1、把DVWA頁面改為low級別&#xff0c;然后點擊File Inclusion頁面 文件包含漏洞有四種include()/require()/include_once()/require_once() 常見的文件包含漏洞代碼如下 <?php$file$_GET[filename]; filename隨意定義include($file); ?> -----…

小程序與物聯網(IoT)融合:開啟智能生活新篇章

一、引言 隨著移動互聯網技術的飛速發展&#xff0c;小程序作為一種輕量級的應用形式&#xff0c;憑借其無需下載安裝、即用即走的特點&#xff0c;迅速滲透到人們生活的各個領域。與此同時&#xff0c;物聯網&#xff08;IoT&#xff09;技術也在不斷進步&#xff0c;將各種物…

Ubuntu無法創建python venv環境

排查步驟如下 1. python3 -m venv venv he virtual environment was not created successfully because ensurepip is not available. On Debian/Ubuntu systems, you need to install the python3-venv package using the following command.apt install python3.8-venvYou…