C# Semaphore与SemaphoreSlim区别小结

 更新时间:2025年12月01日 16:02:26   作者:她说彩礼65万  
本文主要介绍了C# Semaphore与SemaphoreSlim区别小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在 C#(.NET)中,SemaphoreSemaphoreSlim 都用于控制同时访问某一资源或池的线程数量(即“信号量”机制),但它们在实现方式、性能、功能和适用场景上有显著区别。

下面从多个维度详细对比两者,并附使用示例。

一、核心区别概览

特性SemaphoreSemaphoreSlim
命名空间System.ThreadingSystem.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 差异

功能SemaphoreSemaphoreSlim
构造函数Semaphore(initialCount, 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();
        return await client.GetStringAsync(url);
    }
    finally
    {
        _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 包裹。

✅ 总结

对比项SemaphoreSemaphoreSlim
定位通用、跨进程高性能、进程内
灵魂特性跨进程异步支持(WaitAsync)
现代 .NET 首选仅当需要跨进程时✅ 绝大多数场景

🎯 默认选择 SemaphoreSlim;只有需要跨进程时才用 Semaphore。

到此这篇关于C# Semaphore与SemaphoreSlim区别小结的文章就介绍到这了,更多相关C# Semaphore SemaphoreSlim内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于C#编写计算机选课程序

    基于C#编写计算机选课程序

    在这篇文章中将介绍如何开发一个简单的计算机选课程序,文中主要使用了C#语言和Windows Forms来构建这个应用程序,感兴趣的小伙伴可以了解下
    2024-10-10
  • C#中 nameof 和 ToString () 的用法与区别解析

    C#中 nameof 和 ToString () 的用法与区别解析

    在 C# 编程过程中,nameof运算符和ToString()方法是开发者经常会用到的功能,本文将通过具体的代码示例,详细介绍nameof和枚举结合的用法,以及nameof与ToString()之间的区别,感兴趣的朋友一起看看吧
    2025-06-06
  • C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法

    C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法

    这篇文章主要介绍了C# WinForm控件对透明图片重叠时出现图片不透明的简单解决方法,结合实例形式分析了WinForm图片重叠后造成图片不透明的原因与相应的解决方法,需要的朋友可以参考下
    2016-06-06
  • C#用记事本编写简单WinForm窗体程序

    C#用记事本编写简单WinForm窗体程序

    这篇文章主要为大家详细介绍了C#用记事本编写简单WinForm窗体程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • C#具名参数(Named Parameters)的使用

    C#具名参数(Named Parameters)的使用

    在C#中,具名参数是一种在方法调用中使用参数名称来指定参数值的技术,本文主要介绍了C#具名参数的使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • C#通过反射获取当前工程中所有窗体并打开的方法

    C#通过反射获取当前工程中所有窗体并打开的方法

    这篇文章主要介绍了C#通过反射获取当前工程中所有窗体并打开的方法,涉及C#针对窗体的获取与显示等操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-08-08
  • C#聊天程序服务端与客户端完整实例代码

    C#聊天程序服务端与客户端完整实例代码

    这篇文章主要介绍了C#聊天程序服务端与客户端完整实例代码,很经典的应用,需要的朋友可以参考下
    2014-07-07
  • HttpWebRequest实现下载图片至本地

    HttpWebRequest实现下载图片至本地

    这篇文章主要为大家详细介绍了HttpWebRequest实现下载图片至本地,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-07-07
  • C#程序员最易犯的编程错误

    C#程序员最易犯的编程错误

    这篇文章主要介绍了C#程序员最易犯的10个编程错误,了解这些错误能够更好地学习C#程序设计,感兴趣的小伙伴们可以参考一下
    2015-11-11
  • C#通过chrome插件将HTML网页转换为PDF

    C#通过chrome插件将HTML网页转换为PDF

    这篇文章主要介绍了C#通过chrome插件将HTML网页转换为PDF,将HTML网页内容转换为 PDF 格式能方便文档的后续打印、存档和分享等,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2025-03-03

最新评论