ASP.NET Core异步方法非可重入:lock与SemaphoreSlim选型指南
异步方法防并发:lock 和 SemaphoreSlim 怎么选?
lock 是什么情况?
- lock是同步场景里的老伙计,基于Monitor实现,只能用在纯同步代码里。
- 要是你在async方法里用lock包着带
await的代码,编译器直接给你报错——因为lock要求拿锁的线程必须从头到尾扛到释放锁,可await会让线程跑走,锁就没人管了,肯定出问题。 - 只有当你的
RunAsync里全是同步逻辑、半点儿await都没有的时候,lock才有用武之地。
SemaphoreSlim 为啥更适合异步场景?
- SemaphoreSlim是轻量级信号量,专门支持异步等待(靠
WaitAsync()方法),天生适配async方法。 - 把它的初始计数设为1,就能实现和锁一样的互斥效果,代码大概是这样:
private readonly SemaphoreSlim _mutex = new SemaphoreSlim(1, 1); public async Task RunAsync() { await _mutex.WaitAsync(); try { // 这里写你的异步业务逻辑 } finally { _mutex.Release(); } } - 好处是
await的时候会释放当前线程,不会占着线程池资源不放,ASP.NET Core这种靠线程池吃饭的环境里,能避免不必要的线程阻塞,提升整体并发能力。 - 一定要记得在
finally里调用Release(),哪怕代码抛异常,也能保证信号量被释放,不然就会出现死锁,后面的请求永远拿不到信号量。
你的方法是async,这事儿影响大吗?
- 太影响了!这直接把lock给pass了——lock和
await根本不兼容,硬用要么编译不过,要么暗地里搞出锁泄漏、死锁的烂摊子。 - 异步方法的核心就是线程会在
await的时候切换,SemaphoreSlim的异步等待刚好能跟上这个节奏,不会像lock那样卡死线程,所以是异步场景下的唯一靠谱选择。
最后给个准话
- 如果你的
RunAsync里有await异步操作,直接用SemaphoreSlim,别想lock的事儿。 - 只有当方法全是同步代码,半点儿异步都没有,再考虑lock。
内容的提问来源于stack exchange,提问作者Jonathan Wood
相关产品推荐
相关产品推荐

