SemaphoreSlim是控制Task并发数最直接的方式,通过异步闸门限制同时执行任务数,需在共享作用域初始化、配对使用WaitAsync()和Release(),并在finally中确保释放,避免许可证泄漏。
用 SemaphoreSlim 控制 Task 并发数最直接
绝大多数场景下,你根本不需要配置或继承
。真正要解决的不是“任务往哪调度”,而是“别让 100 个
同时冲进线程池”。
就是这道轻量、异步、可控的闸门。
它不碰线程池底层,不改
,只在业务逻辑入口加一层等待:最多 N 个任务能同时进临界区(比如发 HTTP 请求、查数据库),其余自动排队等
。
表示最多 3 个任务并发执行;第 4 个会
挂起,不占线程
必须在类字段或静态域初始化一次,不能在方法里每次
—— 否则限流完全失效
初始计数不能为 0,否则所有
都永久挂起
务必配对
和
,异常路径也要释放:用
或 C# 8+ 的
(注意不是
,除非你用的是
封装)
别碰自定义 TaskScheduler,除非你在写框架
继承
并重写
或
是高危操作。它和线程池、
续传、调试器诊断深度耦合,稍有不慎就会导致任务丢失、死锁、或
状态错乱。
常见误判是以为“重写
加个计数器就能限流”——其实它只决定“这个
扔给谁执行”,不管“扔多少”“啥时候扔”。真正的并发控制仍得靠上层协调(比如
)。
真实适用场景极少:UI 调度器(如 WinForms/WPF 自定义同步上下文)、测试用确定性调度器、嵌入式环境彻底接管任务分发
.NET 6+ 中
已 internal,公开扩展点只剩抽象基类,兼容性和可维护性变差
内不能做耗时操作(如 I/O、
),否则会阻塞整个调度链路
ParallelOptions.MaxDegreeOfParallelism 只适合 CPU 密集型同步循环
如果你的任务是纯计算(比如图像缩放、JSON 解析、数组排序),且全部是同步方法,
是简单可靠的选择。
C知道
CSDN推出的一款AI技术问答工具
下载
但它对任何含
的异步操作完全无效——
不理解
,也不会 await。强行传
lambda 会导致返回
,实际并发数失控。
HTTP 请求、EF Core
、文件读写等 I/O 场景,
基本没用,必须换
混合场景(比如先并行拉数据,再并行处理):拉数据部分用
,处理部分若为同步计算,才考虑
它限制的是
内部线程调度器的并发度,不影响线程池全局行为
常见错误:释放次数不匹配、未 await、作用域错乱
限流失效往往不是逻辑问题,而是使用姿势错了。三个高频坑点:
调用次数 >
:比如在
块里多调了一次,导致信号量计数溢出,后续所有等待都立即通过,限流形同虚设
忘记
:写成
或直接
,会阻塞线程池线程,引发饥饿甚至死锁
声明在方法内:每次调用都新建实例,每个实例都是独立计数器,根本无法跨调用限流
最稳妥写法是字段级单例 +
,或者封装成带
支持的异步资源管理器(需 C# 12+)。并发控制的脆弱点不在算法,而在这些细节能否稳住。
TaskSchedulerTask.RunSemaphoreSlimSynchronizationContextRelease()SemaphoreSlim(3)await WaitAsync()newWaitAsync()await WaitAsync()Release()try/finallyusingawait usingAsyncSemaphoreTaskSchedulerQueueTaskGetScheduledTasksawaitTaskQueueTaskTaskSemaphoreSlimThreadPoolTaskSchedulerQueueTaskThread.SleepParallel.ForEach(..., new ParallelOptions { MaxDegreeOfParallelism = 4 })awaitParallelTaskasyncTaskSaveChangesAsyncParallelSemaphoreSlimSemaphoreSlimParallelParallelRelease()WaitAsync()tryawaitWaitAsync()semaphore.WaitAsync().Wait()semaphore.Wait()SemaphoreSlimtry/finallyawait using