本文主要是介绍C# Semaphore与SemaphoreSlim区别小结,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《C#Semaphore与SemaphoreSlim区别小结》本文主要介绍了C#Semaphore与SemaphoreSlim区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的...
在 C#(.NET)中,Semaphore 和 Semaphorewww.chinasem.cnSlim 都用于控制同时访问某一资源或池的线程数量(即“信号量”机制),但它们在实现方式、性能、功能和适用场景上有显著区别。
下面从多个维度详细对比两者,并附使用示例。
一、核心区别概览
| 特性 | Semaphore | SemaphoreSlim |
|---|---|---|
| 命名空间 | System.Threading | System.Threading |
| 底层实现 | 内核模式(Windows 内核信号量对象) | 用户模式 + 混合模式(优先自旋,必要时用内核事件) |
| 跨进程支持 | ✅ 支持(可通过名称创建命名信号量) | ❌ 不支持 |
| 异步支持 | ❌ 无 WaitAsync() | ✅ 支持 WaitAsync() |
| 性能 | 较低(每次 Wait/Release 涉及内核切换) | 较高(短竞争无内核开销) |
| 适用场景 | 跨进程同步、长时间等待 | 进程内同步、高性能、异步编程 |
| 是否可重入 | 否 | 否 |
| 释放行为 | 必须由获取线程释放(但无所有权概念) | 同左 |
二、详细对比说明
1.跨进程支持
Semaphore:
// 进程 A
var sem = new Semaphore(2, 2, "MyGlobalSemaphore");
// 进程 B
var sem = Semaphore.OpenExisting("MyGlobalSemaphore");
适用于多个进程共享有限资源(如硬件设备、全局连接池)。
- 可创建命名信号量,供多个进程共享。
SemaphoreSlim:
- 仅限当前进程内使用,无法跨进程。
- 内部使用
ManualResetEventSlim或自旋,无内核对象名称。
2.异步支持(关键区别!)
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、高并发 I/O 场景(如限流)。
Semaphore 只有同步方法 WaitOne(),在异步上下文中会阻塞线程,导致线程池饥饿:
// ❌ 不推荐在 async 方法中使用 sem.WaitOne(); // 阻塞当前线程!
3.性能差异
SemaphoreSlim:- 在无竞争或轻度竞争时,完全在用户态运行,无系统调用。
- 即使有竞争,也先自旋(
SpinWait),失败后才使用轻量内核事件。 - 延迟低、吞吐高。
Semaphore:- 每次
WaitOne()/Release()都触发内核模式切换(约 1000~3000 纳秒开销)。 - 适合低频、长时间持有的场景。
- 每次
性能测试表明:在高频短临界区场景,SemaphoreSlim 比 Semaphore 快 5~10 倍以上。
4.API 差异
| 功能 | Semaphore | SemaphoreSlim |
|---|---|---|
| 构造函数 | Semaphore(initialCChina编程ount, maximumCount, name?) | SemaphoreSlim(initialCount, maxCount?) |
| 等待 | WaitOne(), WaitOne(timeout) | Wait(), Wait(timeout), WaitAsync(), WaitAsync(timeout) |
| 释放 | Release(), Release(count) | Release(), Release(count) |
| 打开现有(跨进程) | OpenExisting(name) | ❌ 不支持 |
三、使用示例对比
✅ 场景:限制并发 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();
http://www.chinasem.cn return await client.GetStringAsync(url);
}
finallphpy
{
_throttle.Release();
}
}
✅ 高效、不阻塞线程池线程,适合 ASP.NET Core 等高并发环境。
✅ 场景:两个进程共享最多 2 个数据库连接(必须用Semaphore)
进程 A:
var sem = new Semaphore(2, 2, "Global\\DBConnectionPool"); sem.WaitOne(); // 使用数据库连接... sem.Release();
进程 B:
var sem = Semaphore.OpenExisting("Global\\DBConnectionPool");
sem.WaitOne();
// 使用数据库连接...
sem.Release();
✅ 只有
Semaphore能跨进程协调资源。
四、如何选择?
| 需求 | 推荐类型 |
|---|---|
| 进程内同步 + 异步支持 | ✅ SemaphoreSlim |
| 高性能、低延迟 | ✅ SemaphoreSlim |
| 跨进程同步 | ✅ Semaphore |
| 长时间持有信号量 | ⚠️ 两者皆可,但 Semaphore 更稳定 |
| Web 服务限流、API 调用控制 | ✅ SemaphoreSlim(配合 WaitAsync) |
| 桌面应用多实例共享资源 | ✅ Semaphore(命名) |
五、注意事项
1.SemaphoreSlim不是线程安全的“计数器”
- 它只控制进入数量,不保证操作原子性。
- 临界区内仍需其他同步机制(如
lock)保护共享数据。
2.避免在SemaphoreSlim中阻塞线程
// ❌ 错误:在 WaitAsync 后又同步阻塞 await _sem.WaitAsync(); Thread.Sleep(1000); // 浪费线程!应使用 await Task.Delay(1000)
3.Release()调用次数不能超过Wait()
- 否则会抛出
SemaphoreFullException。 - 建议始终用
try/finally包裹。
✅ 总结
| 对比项 | Semaphore | SemaphoreSlim |
|---|---|---|
| 定位 | 通用、跨进程 | 高性能、进程内 |
| 灵魂特性 | 跨进程 | 异步支js持(WaitAsync) |
| 现代 .NET 首选 | 仅当需要跨进程时 | ✅ 绝大多数场景 |
默认选择 SemaphoreSlim;只有需要跨进程时才用 Semaphore。
到此这篇关于C# Semaphore与SemaphoreSlim区别小结的文章就介绍到这了,更多相关C# Semaphore SemaphoreSlim内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!
这篇关于C# Semaphore与SemaphoreSlim区别小结的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!