深入理解 SemaphoreSlim 在.NET Core API 開發中的應用

目錄

什么是 SemaphoreSlim

SemaphoreSlim 的核心方法

構造函數

等待方法

釋放方法

基本使用模式

同步使用模式

異步使用模式(推薦在 API 中使用)

在 Web 開發中的常見用途

1. 限制 API 接口的并發請求數

2. 保護共享資源的并發訪問

3. 控制外部服務的調用頻率

4. 實現分布式鎖的本地補充

注意事項與最佳實踐

1. 確保正確釋放信號量

2. 合理設置信號量的生命周期

3. 避免過度限制并發

4. 注意異步操作中的取消機制

5. 警惕死鎖風險

6. 結合限流中間件使用

7. 多實例部署的局限性

總結


在.NET Core API 開發中,并發控制是保證系統穩定性和數據一致性的關鍵環節。當多個請求同時訪問共享資源時,若不加以控制,很容易引發數據錯亂、性能下降等問題。SemaphoreSlim作為.NET 框架中輕量級的信號量實現,為我們提供了簡單而高效的并發控制方案。本文將詳細介紹 SemaphoreSlim 的工作原理、使用方法以及在 Web 開發中的常見應用場景。

什么是 SemaphoreSlim

SemaphoreSlim 是.NET Framework 4.0 引入的輕量級信號量類,位于System.Threading命名空間下。它專為短期等待場景設計,相比傳統的Semaphore類,具有更低的系統開銷和更高的性能。

信號量的核心作用是限制同時訪問某個資源的并發數量。可以將其理解為一個帶計數器的門控機制:當計數器大于 0 時,允許線程進入并將計數器減 1;當計數器為 0 時,后續線程必須等待直到有線程釋放資源(計數器加 1)。

與傳統 Semaphore 相比,SemaphoreSlim 的優勢在于:

  • 不依賴操作系統內核對象,減少了用戶態與內核態之間的切換開銷
  • 支持異步等待(WaitAsync方法),非常適合異步 API 開發
  • 內存占用更小,創建和銷毀的成本更低
  • 支持取消令牌(CancellationToken),便于實現超時控制和操作取消

SemaphoreSlim 的核心方法

SemaphoreSlim 提供了一組簡潔而強大的方法來實現并發控制,掌握這些方法是正確使用 SemaphoreSlim 的基礎:

構造函數

// 初始化一個可同時允許maxCount個線程訪問的信號量
public SemaphoreSlim(int initialCount);
public SemaphoreSlim(int initialCount, int maxCount);
  • initialCount:信號量的初始計數,即初始允許的并發數量
  • maxCount:信號量的最大計數,規定了允許的最大并發數量

等待方法

// 同步等待,直到信號量可用
public void Wait();
public bool Wait(int millisecondsTimeout);
public bool Wait(TimeSpan timeout);
public void Wait(CancellationToken cancellationToken);// 異步等待,適合異步方法中使用
public Task WaitAsync();
public Task<bool> WaitAsync(int millisecondsTimeout);
public Task<bool> WaitAsync(TimeSpan timeout);
public Task WaitAsync(CancellationToken cancellationToken);

等待方法的作用是申請訪問權限:當調用這些方法時,線程會嘗試獲取信號量。如果當前計數大于 0,計數減 1 并立即返回;否則,線程會進入等待狀態,直到有其他線程釋放信號量或等待超時 / 被取消。

釋放方法

// 釋放信號量,增加計數
public void Release();
public int Release(int releaseCount);

釋放方法用于歸還訪問權限:當線程完成對共享資源的訪問后,應調用Release方法將信號量計數加 1,允許其他等待的線程獲取訪問權限。Release(int releaseCount)可以一次釋放多個計數,但釋放的總數不能超過maxCount。

基本使用模式

使用 SemaphoreSlim 的核心原則是 **"先等待,后訪問,最后釋放"**,通常遵循以下模式:

同步使用模式

// 創建一個最多允許3個并發訪問的信號量
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3);public void AccessResource()
{try{// 等待獲取信號量_semaphore.Wait();// 訪問共享資源的邏輯UseSharedResource();}finally{// 釋放信號量,必須放在finally中確保一定會執行_semaphore.Release();}
}

異步使用模式(推薦在 API 中使用)

private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3);public async Task AccessResourceAsync()
{try{// 異步等待獲取信號量await _semaphore.WaitAsync();// 異步訪問共享資源的邏輯await UseSharedResourceAsync();}finally{// 釋放信號量_semaphore.Release();}
}

在 Web 開發中的常見用途

在.NET Core API 開發中,SemaphoreSlim 有多種實用場景,尤其適合處理那些需要限制并發量的操作:

1. 限制 API 接口的并發請求數

當 API 接口調用涉及到資源密集型操作(如復雜計算、大數據處理)時,無限制的并發可能導致服務器資源耗盡。使用 SemaphoreSlim 可以限制同時處理的請求數量:

[ApiController]
[Route("api/[controller]")]
public class DataProcessorController : ControllerBase
{// 限制最多5個并發請求private static readonly SemaphoreSlim _processorSemaphore = new SemaphoreSlim(5);[HttpPost("process")]public async Task<IActionResult> ProcessData([FromBody] DataRequest request){try{// 等待獲取處理權限,設置5秒超時if (!await _processorSemaphore.WaitAsync(5000)){return StatusCode(StatusCodes.Status429TooManyRequests, "當前請求過多,請稍后再試");}// 處理數據var result = await DataProcessor.ProcessAsync(request);return Ok(result);}finally{_processorSemaphore.Release();}}
}

2. 保護共享資源的并發訪問

當多個請求需要訪問同一個共享資源(如文件、非線程安全的服務實例)時,SemaphoreSlim 可以確保資源操作的原子性:

public class FileService
{private readonly SemaphoreSlim _fileAccessSemaphore = new SemaphoreSlim(1);private readonly string _filePath = "data.json";public async Task UpdateFileAsync(string content){await _fileAccessSemaphore.WaitAsync();try{// 讀取當前內容var currentContent = await File.ReadAllTextAsync(_filePath);// 處理內容var newContent = ProcessContent(currentContent, content);// 寫入新內容await File.WriteAllTextAsync(_filePath, newContent);}finally{_fileAccessSemaphore.Release();}}
}

3. 控制外部服務的調用頻率

調用第三方 API 時,通常會有頻率限制(Rate Limiting)。使用 SemaphoreSlim 可以控制并發調用數量,避免觸發限制:

public class ExternalApiClient
{private readonly HttpClient _httpClient;// 根據第三方API的限制設置并發數private readonly SemaphoreSlim _apiSemaphore = new SemaphoreSlim(10);private readonly int _apiTimeoutMs = 3000;public ExternalApiClient(HttpClient httpClient){_httpClient = httpClient;}public async Task<TResult> CallApiAsync<TResult>(string endpoint){try{// 等待獲取調用權限if (!await _apiSemaphore.WaitAsync(_apiTimeoutMs)){throw new TimeoutException("等待調用第三方API超時");}// 調用外部APIvar response = await _httpClient.GetAsync(endpoint);response.EnsureSuccessStatusCode();return await response.Content.ReadFromJsonAsync<TResult>();}finally{_apiSemaphore.Release();}}
}

4. 實現分布式鎖的本地補充

在分布式系統中,雖然需要分布式鎖(如 Redis 鎖)來跨實例同步,但結合 SemaphoreSlim 可以減少分布式鎖的競爭:

public class DistributedTaskService
{private readonly IDistributedLock _distributedLock;// 每個實例最多處理2個任務,減少分布式鎖競爭private readonly SemaphoreSlim _localSemaphore = new SemaphoreSlim(2);public async Task ExecuteTaskAsync(string taskId){// 先獲取本地信號量,減少分布式鎖的競爭await _localSemaphore.WaitAsync();try{// 再獲取分布式鎖using (var lockResult = await _distributedLock.AcquireLockAsync(taskId)){if (lockResult.Success){await ProcessTaskAsync(taskId);}}}finally{_localSemaphore.Release();}}
}

注意事項與最佳實踐

雖然 SemaphoreSlim 使用簡單,但在實際應用中仍需注意以下幾點,以避免常見問題:

1. 確保正確釋放信號量

最常見的錯誤是忘記釋放信號量或在異常情況下未能釋放,這會導致信號量計數永遠無法恢復,最終所有請求都陷入無限等待。因此,務必將Release調用放在finally塊中,確保無論是否發生異常都會執行。

2. 合理設置信號量的生命周期

在 Web 應用中,SemaphoreSlim 通常應聲明為static或使用單例模式,以確保它在應用生命周期內保持狀態。如果每次請求都創建新的 SemaphoreSlim 實例,將失去并發控制的作用。

// 正確:靜態字段,應用域內共享
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(5);// 錯誤:每次實例化都會創建新的信號量
public class MyController : ControllerBase
{private readonly SemaphoreSlim _badSemaphore = new SemaphoreSlim(5);// ...
}

3. 避免過度限制并發

信號量的作用是 "限制" 而非 "禁止" 并發,設置過小的并發數會導致系統吞吐量下降。應根據實際負載測試結果,結合服務器資源(CPU、內存、IO 等)合理設置最大并發數。

4. 注意異步操作中的取消機制

在異步場景中,建議使用帶CancellationToken的WaitAsync重載,以便在請求被取消(如客戶端斷開連接)時能夠及時釋放資源:

// 結合HTTP請求的取消令牌
public async Task<IActionResult> MyAction()
{try{// 使用RequestAborted令牌await _semaphore.WaitAsync(ControllerContext.HttpContext.RequestAborted);// ...}finally{_semaphore.Release();}
}

5. 警惕死鎖風險

當使用多個信號量時,可能會出現死鎖:線程 A 持有信號量 1 并等待信號量 2,而線程 B 持有信號量 2 并等待信號量 1。避免這種情況的方法是:

  • 盡量使用單個信號量解決問題
  • 如果必須使用多個信號量,確保所有線程按相同的順序獲取它們

6. 結合限流中間件使用

對于 API 級別的全局限流,SemaphoreSlim 可以與ASP.NET Core 的限流中間件配合使用,但注意不要重復限流導致性能問題。

7. 多實例部署的局限性

需要注意的是,SemaphoreSlim 是進程內的并發控制機制,無法跨多個應用實例同步。在多服務器、多容器部署的場景中,還需要結合分布式鎖(如基于 Redis 或 ZooKeeper 的實現)才能實現全局的并發控制。

總結

SemaphoreSlim 是.NET Core API 開發中處理并發控制的得力工具,它輕量高效,支持異步操作,非常適合 Web 環境下的短期等待場景。通過合理使用 SemaphoreSlim,我們可以有效限制資源訪問的并發數量,保護共享資源,控制外部服務調用頻率,從而提高系統的穩定性和可靠性。

使用 SemaphoreSlim 的核心在于理解 "獲取 - 使用 - 釋放" 的模式,并始終牢記在finally塊中釋放信號量。同時,也要認識到它在多實例部署中的局限性,必要時結合分布式方案進行補充。掌握這些知識,將幫助你在實際開發中更好地應對并發挑戰,構建更健壯的 API 服務。

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

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

相關文章

板凳-------Mysql cookbook學習 (十二--------4)

11.0 概述 386 11.1 使用LOAD DATA和mysqlimport導入數據 390 首先創建 mytbl_3 表&#xff08;結構與 mytbl 相同&#xff09;&#xff1a;sql CREATE TABLE mytbl_3 LIKE mytbl;用文本編輯器&#xff08;如 Notepad&#xff09;打開 mytbl.txt&#xff0c;確保格式轉換成wind…

【Git#6】多人協作 企業級開發模型

一、多人協作 1. 同一分支下的協作 目前&#xff0c;我們所完成的工作如下&#xff1a; 基本完成 Git 的所有本地庫的相關操作&#xff0c;git基本操作&#xff0c;分支理解&#xff0c;版本回退&#xff0c;沖突解決等等申請碼云賬號&#xff0c;將遠端信息clone到本地&…

C# 中的強大運算符

C# 中鮮為人知的強大運算符 C# 還提供了一些"冷門"但功能強大的運算符&#xff0c;這些運算符在特定場景下能極大簡化代碼并提高效率。 1. 空合并賦值運算符 ?? // 傳統寫法 if (variable null) {variable defaultValue; }// 使用 ?? variable ?? defaultVal…

用window字體替換zabbix 默認的字體

我們先需要在windows系統下的C:\Windows\Fonts目錄&#xff0c;找到一個喜歡的字體&#xff0c;我選擇的是微軟雅黑。復制到其它路徑下&#xff0c;選取一個msyh.ttc。到服務器上。要把msyh.ttc改為msyh.ttf才可以。不然最后中英文都不顯示[roothadoop105.yinzhengjie.com ~]# …

MySQL 17 如何正確地顯示隨機消息?

假設有一個場景&#xff0c;一個英語學習APP首頁有一個隨機顯示單詞的功能&#xff0c;用戶每次訪問首頁的時候&#xff0c;都會隨機滾動顯示三個單詞。 已知表里有10000條記錄&#xff0c;來看看隨機選擇3個單詞有什么方法&#xff0c;又存在什么問題。 建表語句&#xff1a…

7-Zip 曝出兩個可導致拒絕服務的中危漏洞

研究人員在全球使用最廣泛的開源文件壓縮軟件7-Zip中新發現兩個漏洞&#xff08;CVE-2025-53816和CVE-2025-53817&#xff09;。這兩個漏洞影響7-Zip 25.0.0之前的所有版本&#xff0c;雖然不能實現遠程代碼執行&#xff0c;但可能引發內存損壞和拒絕服務&#xff08;Denial of…

史上最簡單Conda+Ollama+Open-Webui安裝方法!

史上最簡單CondaOllamaOpen-Webui安裝方法 一、安裝Anaconda 1、到Anaconda官網下載conda_24.10.1 鏈接&#xff1a;https://repo.anaconda.com/archive/Anaconda3-2024.10-1-Windows-x86_64.exe 2.雙擊安裝包&#xff0c;開始安裝 選擇All Users 切記安裝路徑不要選C盤&am…

Python-數據庫概念-pymysql-元編程-SQLAlchemy-學習筆記

序 欠4前年的一份筆記 &#xff0c;獻給今后的自己。 數據庫 概念 數據庫&#xff1a;按照數據結構來組織、存儲、管理數據的倉庫。 誕生 計算機的發明是為了做科學計算的&#xff0c;而科學計算需要大量的數據輸入和輸出。 早期&#xff0c;可以使用打孔卡片的孔、燈泡的亮滅來…

Linux入門篇學習——借助 U 盤或 TF 卡拷貝程序到開發板上

借助 U 盤或 TF 卡拷貝程序到開發板上我們已經學習了怎么在 ubuntu 和 windows 上互傳文件&#xff0c;那么怎么把 ubuntu 或 win 上的程序拷貝到開發板呢&#xff0c;這里給大家介紹第一種方法&#xff0c;使用 U 盤或者 TF 卡來完成&#xff0c;如果大家使用的是 U 盤&#x…

【親測有效】防檢測插件playwright_stealth 2.X版本快速使用

這里寫自定義目錄標題核心方法apply_stealth_syncuse_sync和use_async一. playwright_stealth 2.0以上版本1.同步方法2.異步方法3.實例二.playwright_stealth 2.0以下版本playwright-stealth 是一個用于 Playwright 的庫&#xff0c;旨在幫助自動化腳本避開一些檢測機制&#x…

docker安裝與簡單項目上手

1.docker安裝 系統版本為almalinux9.6 首先添加一下docker的軟件安裝源&#xff08;源選擇的阿里云&#xff0c;只要是rhel的系統都適用&#xff0c;無論是rockylinux還是almalinux還是紅帽企業版&#xff09; dnf config-manager --add-repo https://mirrors.aliyun.com/doc…

計算機網絡基礎:從協議到通信全解析(大致框架)

本節重點&#xff1a;1.了解網絡發展背景&#xff0c;對局域網/廣域網的概念有基本認識2.了解網絡協議的意義&#xff0c;重點理解TCP/IP五層結構模型3.學習網絡傳輸的基本流程&#xff0c;理解封裝和解包分用一、計算機網絡發展背景&#xff1a;人與人之間是需要協同工作的&am…

PDF 編輯器:多文件合并 拆分 旋轉 順序隨便調 加水印 密碼鎖 頁碼背景

各位打工人、學生黨們&#xff0c;你們是不是也遇到過這種情況&#xff0c;領導甩來一個PDF讓你改&#xff0c;結果你搗鼓半天&#xff0c;發現這玩意兒根本動不了&#xff0c;簡直想原地爆炸&#xff01;別急別急&#xff0c;今天就給你們安利一個辦公軟件——PDF編輯器&#…

【軟件基礎學習配置那些事 4-3】3ds Max2026 菜單欄常用命令-----文件、視圖、編輯、工具、組

3ds Max學習的筆記小知識&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;后續都會補充添加&#xff01;&#xff01;&#xff01;&#xff01;&#xff08;個人的一些學習筆記&#xff0c;如有不對&#xff0c;歡迎訂正&am…

網絡爬蟲的介紹

網絡爬蟲庫網絡爬蟲通俗來講就是使用代碼將HTML網頁的內容下載到本地的過程。爬取網頁主要是為了獲取網中的關鍵信息&#xff0c;例如網頁中的數據、圖片、視頻等。Python語言中提供了多個具有爬蟲功能的庫&#xff0c;下面將具的介紹。urlib庫:是Python自帶的標準庫&#xff0…

C# 編程實戰進階:字符串與字符串數組 (3)

目錄 1、給定一個整數數組 nums&#xff0c;將數組中的元素向右輪轉 k 個位置&#xff0c;其中 k 是非負數。 2、無重復字符的最長字符串 &#xff0c;給定一個字符串 s 請你找出其中不含有重復字符的最長字符串的長度。 3、給定兩個字符串 s 和 t &#xff0c;它們只包含小…

Python趣味算法:百錢百雞問題——雙重循環優化與算法效率分析

如何用Python解決中國古代數學難題?本文從暴力枚舉到高效優化,帶你領略算法之美,效率提升100倍! 看在每天堅持分享有趣知識的份上,點個關注吧(づ ̄ 3 ̄)づ 關注是我更新的動力 ̄︶ ̄? ̄︶ ̄?) 作者會分享更多涉及到各種編程語言的有趣知識!(^?^●)?? 目錄 …

JAVA_TWO-初識Java2

1.IDEA管理Java程序的結構2.idea編譯后的class文件在哪在工程out文件夾下。3.idea一些快捷鍵4.導入模塊File→New→Module from Existing Sources → 添加后綴.iml文件5.注釋單行注釋 //多行注釋 /* 注釋內容1注釋內容2 */文檔注釋 /** 注釋內容 */ &#xff08;文檔注釋內容可…

二、Dify 版本升級教程(LInux-openeuler)

首先&#xff0c;你需要先按照好dify&#xff0c;然后才能升級&#xff0c;本文教程是基與Docker Compose 如果你還沒有安裝&#xff0c;可以看看這個教程。 一、Dify 私有部署、本地安裝教程&#xff08;LInux-openeuler&#xff09;_dify1.5版本部署-CSDN博客 安裝完成后&a…

Java 大視界 -- Java 大數據在智能安防門禁系統中的多生物特征融合識別與權限管理(280)

??親愛的朋友們,熱烈歡迎來到 青云交的博客!能與諸位在此相逢,我倍感榮幸。在這飛速更迭的時代,我們都渴望一方心靈凈土,而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識,也期待你毫無保留地分享獨特見解,愿我們于此攜手成長,共赴新程!?? 本博…