当前位置: 代码网 > it编程>编程语言>Asp.net > C# Semaphore与SemaphoreSlim区别小结

C# Semaphore与SemaphoreSlim区别小结

2025年12月01日 Asp.net 我要评论
在 c#(.net)中,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内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com