C# Semaphore與SemaphoreSlim區(qū)別小結(jié)
在 C#(.NET)中,Semaphore 和 SemaphoreSlim 都用于控制同時訪問某一資源或池的線程數(shù)量(即“信號量”機制),但它們在實現(xiàn)方式、性能、功能和適用場景上有顯著區(qū)別。
下面從多個維度詳細對比兩者,并附使用示例。
一、核心區(qū)別概覽
| 特性 | Semaphore | SemaphoreSlim |
|---|---|---|
| 命名空間 | System.Threading | System.Threading |
| 底層實現(xiàn) | 內(nèi)核模式(Windows 內(nèi)核信號量對象) | 用戶模式 + 混合模式(優(yōu)先自旋,必要時用內(nèi)核事件) |
| 跨進程支持 | ? 支持(可通過名稱創(chuàng)建命名信號量) | ? 不支持 |
| 異步支持 | ? 無 WaitAsync() | ? 支持 WaitAsync() |
| 性能 | 較低(每次 Wait/Release 涉及內(nèi)核切換) | 較高(短競爭無內(nèi)核開銷) |
| 適用場景 | 跨進程同步、長時間等待 | 進程內(nèi)同步、高性能、異步編程 |
| 是否可重入 | 否 | 否 |
| 釋放行為 | 必須由獲取線程釋放(但無所有權(quán)概念) | 同左 |
二、詳細對比說明
1.跨進程支持
Semaphore:
// 進程 A
var sem = new Semaphore(2, 2, "MyGlobalSemaphore");
// 進程 B
var sem = Semaphore.OpenExisting("MyGlobalSemaphore");
適用于多個進程共享有限資源(如硬件設備、全局連接池)。
- 可創(chuàng)建命名信號量,供多個進程共享。
SemaphoreSlim:
- 僅限當前進程內(nèi)使用,無法跨進程。
- 內(nèi)部使用
ManualResetEventSlim或自旋,無內(nèi)核對象名稱。
2.異步支持(關(guān)鍵區(qū)別?。?/h3>
SemaphoreSlim 提供 WaitAsync() 方法,完美支持 async/await:
private static SemaphoreSlim _sem = new SemaphoreSlim(3);
public async Task ProcessAsync()
{
await _sem.WaitAsync(); // 異步等待,不阻塞線程
try
{
await CallExternalApiAsync(); // 模擬 I/O 操作
}
finally
{
_sem.Release();
}
}
? 非常適合 Web API、高并發(fā) I/O 場景(如限流)。
Semaphore 只有同步方法 WaitOne(),在異步上下文中會阻塞線程,導致線程池饑餓:
// ? 不推薦在 async 方法中使用 sem.WaitOne(); // 阻塞當前線程!
3.性能差異
SemaphoreSlim:- 在無競爭或輕度競爭時,完全在用戶態(tài)運行,無系統(tǒng)調(diào)用。
- 即使有競爭,也先自旋(
SpinWait),失敗后才使用輕量內(nèi)核事件。 - 延遲低、吞吐高。
Semaphore:- 每次
WaitOne()/Release()都觸發(fā)內(nèi)核模式切換(約 1000~3000 納秒開銷)。 - 適合低頻、長時間持有的場景。
- 每次
?? 性能測試表明:在高頻短臨界區(qū)場景,SemaphoreSlim 比 Semaphore 快 5~10 倍以上。
4.API 差異
| 功能 | Semaphore | SemaphoreSlim |
|---|---|---|
| 構(gòu)造函數(shù) | Semaphore(initialCount, maximumCount, name?) | SemaphoreSlim(initialCount, maxCount?) |
| 等待 | WaitOne(), WaitOne(timeout) | Wait(), Wait(timeout), WaitAsync(), WaitAsync(timeout) |
| 釋放 | Release(), Release(count) | Release(), Release(count) |
| 打開現(xiàn)有(跨進程) | OpenExisting(name) | ? 不支持 |
三、使用示例對比
? 場景:限制并發(fā) HTTP 請求(推薦用SemaphoreSlim)
// 使用 SemaphoreSlim(支持異步)
private static readonly SemaphoreSlim _throttle = new SemaphoreSlim(5, 5);
public async Task<string> FetchDataAsync(string url)
{
await _throttle.WaitAsync(); // 異步等待,不占線程
try
{
using var client = new HttpClient();
return await client.GetStringAsync(url);
}
finally
{
_throttle.Release();
}
}
? 高效、不阻塞線程池線程,適合 ASP.NET Core 等高并發(fā)環(huán)境。
? 場景:兩個進程共享最多 2 個數(shù)據(jù)庫連接(必須用Semaphore)
進程 A:
var sem = new Semaphore(2, 2, "Global\\DBConnectionPool"); sem.WaitOne(); // 使用數(shù)據(jù)庫連接... sem.Release();
進程 B:
var sem = Semaphore.OpenExisting("Global\\DBConnectionPool");
sem.WaitOne();
// 使用數(shù)據(jù)庫連接...
sem.Release();
? 只有
Semaphore能跨進程協(xié)調(diào)資源。
四、如何選擇?
| 需求 | 推薦類型 |
|---|---|
| 進程內(nèi)同步 + 異步支持 | ? SemaphoreSlim |
| 高性能、低延遲 | ? SemaphoreSlim |
| 跨進程同步 | ? Semaphore |
| 長時間持有信號量 | ?? 兩者皆可,但 Semaphore 更穩(wěn)定 |
| Web 服務限流、API 調(diào)用控制 | ? SemaphoreSlim(配合 WaitAsync) |
| 桌面應用多實例共享資源 | ? Semaphore(命名) |
五、注意事項
1.SemaphoreSlim不是線程安全的“計數(shù)器”
- 它只控制進入數(shù)量,不保證操作原子性。
- 臨界區(qū)內(nèi)仍需其他同步機制(如
lock)保護共享數(shù)據(jù)。
2.避免在SemaphoreSlim中阻塞線程
// ? 錯誤:在 WaitAsync 后又同步阻塞 await _sem.WaitAsync(); Thread.Sleep(1000); // 浪費線程!應使用 await Task.Delay(1000)
3.Release()調(diào)用次數(shù)不能超過Wait()
- 否則會拋出
SemaphoreFullException。 - 建議始終用
try/finally包裹。
? 總結(jié)
| 對比項 | Semaphore | SemaphoreSlim |
|---|---|---|
| 定位 | 通用、跨進程 | 高性能、進程內(nèi) |
| 靈魂特性 | 跨進程 | 異步支持(WaitAsync) |
| 現(xiàn)代 .NET 首選 | 僅當需要跨進程時 | ? 絕大多數(shù)場景 |
?? 默認選擇 SemaphoreSlim;只有需要跨進程時才用 Semaphore。
到此這篇關(guān)于C# Semaphore與SemaphoreSlim區(qū)別小結(jié)的文章就介紹到這了,更多相關(guān)C# Semaphore SemaphoreSlim內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用C#實現(xiàn)將CSV數(shù)據(jù)輕松轉(zhuǎn)換為PDF
將 CSV 數(shù)據(jù)轉(zhuǎn)換為 PDF 格式在許多業(yè)務中是一個常見的需求,在這篇文章中,我們將探討如何使用 使用 C# 和 Spire.XLS for .NET 庫高效地將 CSV 文件轉(zhuǎn)換為 PDF,希望對大家有所幫助2025-11-11
C#使用NPOI實現(xiàn)Excel讀取數(shù)據(jù)以及保存數(shù)據(jù)
這篇文章主要為大家詳細介紹了C#如何使用core版本的NPOI實現(xiàn)Excel讀取數(shù)據(jù)以及保存數(shù)據(jù)功能,文中的示例代碼講解詳細,感興趣的可以了解一下2025-03-03
C#中JavaScriptSerializer幫助類用法實例
這篇文章主要介紹了C#中JavaScriptSerializer幫助類用法,實例分析了JavaScriptSerializer幫助類處理json字符串時的技巧,需要的朋友可以參考下2014-12-12

