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#实现将CSV数据轻松转换为PDF

    使用C#实现将CSV数据轻松转换为PDF

    将 CSV 数据转换为 PDF 格式在许多业务中是一个常见的需求,在这篇文章中,我们将探讨如何使用 使用 C# 和 Spire.XLS for .NET 库高效地将 CSV 文件转换为 PDF,希望对大家有所帮助
    2025-11-11
  • C#集合之可观察集合的用法

    C#集合之可观察集合的用法

    这篇文章介绍了C#集合之可观察集合的用法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • unity学习教程之定制脚本模板示例代码

    unity学习教程之定制脚本模板示例代码

    这篇文章主要给大家介绍了关于unity学习教程之定制脚本模板的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-12-12
  • C#实现同步模式下的端口映射程序

    C#实现同步模式下的端口映射程序

    这篇文章介绍了C#实现同步模式下的端口映射程序,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • c#判断输入的是不是数字的小例子

    c#判断输入的是不是数字的小例子

    c#判断输入的是不是数字的小例子,需要的朋友可以参考一下
    2013-03-03
  • Unity3D实现模型淡入淡出效果

    Unity3D实现模型淡入淡出效果

    这篇文章主要为大家详细介绍了Unity3D实现模型淡出效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-02-02
  • C#使用NPOI实现Excel读取数据以及保存数据

    C#使用NPOI实现Excel读取数据以及保存数据

    这篇文章主要为大家详细介绍了C#如何使用core版本的NPOI实现Excel读取数据以及保存数据功能,文中的示例代码讲解详细,感兴趣的可以了解一下
    2025-03-03
  • C#基础知识之FileStream

    C#基础知识之FileStream

    C#中FileStream对象表示在磁盘或网络路径上指向文件的流。可以使用FileStream 类对文件系统上的文件进行读取、写入、打开、关闭等。下面我们就来详细探讨下
    2016-07-07
  • C#实现随机数产生类实例

    C#实现随机数产生类实例

    这篇文章主要介绍了C#实现随机数产生类,实例分析了C#随机数的实现技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-03-03
  • C#中JavaScriptSerializer帮助类用法实例

    C#中JavaScriptSerializer帮助类用法实例

    这篇文章主要介绍了C#中JavaScriptSerializer帮助类用法,实例分析了JavaScriptSerializer帮助类处理json字符串时的技巧,需要的朋友可以参考下
    2014-12-12

最新评论