如何根据变量实现ASP.NET MVC控制器动作的分组锁定?
按ID分组锁定MVC动作代码块的实现方案
没问题,这个需求完全可以实现!你现在的全局锁会让所有请求串行执行,而我们只需要基于ID创建独立的锁对象,让相同ID的请求共享同一把锁,不同ID的请求使用不同的锁,就能实现同ID串行、异ID并行的效果。
核心思路
用线程安全的字典(ConcurrentDictionary)来存储每个ID对应的锁对象:
- 当请求进来时,根据当前ID从字典中获取对应的锁对象;如果不存在,就创建一个新的锁对象并存入字典。
- 用这个ID专属的锁对象执行
lock操作,这样相同ID的请求会排队等待,不同ID的请求则可以并行处理。
具体代码实现
public class ProductsController : Controller { // 线程安全的字典,存储ID与对应锁对象的映射 private static readonly ConcurrentDictionary<int, object> _idLocks = new ConcurrentDictionary<int, object>(); public ActionResult MyAction(int id) { // 原子操作:获取或创建对应ID的锁对象,确保同一ID只会有一个锁实例 var lockObject = _idLocks.GetOrAdd(id, key => new object()); lock (lockObject) { // 这里处理你的业务逻辑 // 相同ID的请求会串行执行,不同ID的请求可并行执行 // do my work } // 可选:清理不再使用的锁对象(按需使用) // 注意:频繁移除可能影响性能,若ID范围固定或请求频繁,建议跳过此步骤 // _idLocks.TryRemove(id, out _); } }
关键细节说明
为什么用
ConcurrentDictionary?
它的GetOrAdd方法是原子操作,能避免多线程同时创建同一ID的锁对象,保证每个ID只会对应一个锁实例。如果用普通Dictionary,多线程环境下会出现线程安全问题。不能直接锁ID本身
比如lock(id)是不可行的:因为int是值类型,每次装箱会生成不同的对象,锁不住相同ID的请求;而且如果其他代码也用相同数值的int做锁,会导致意外的互斥。内存占用问题
如果系统中有大量不同的ID,字典会积累很多锁对象。如果需要优化内存,可以:- 使用
WeakReference<object>存储锁对象,让GC在锁对象不再被引用时回收它。 - 定期清理字典中长时间未使用的锁对象(可以结合定时任务实现)。
- 使用
分布式场景注意
如果你的MVC应用部署在多台服务器上,本地锁就无法跨服务器生效了,这时候需要用分布式锁(比如基于Redis的RedLock方案),不过单服务器场景下上面的代码完全够用。
内容的提问来源于stack exchange,提问作者mini998
相关产品推荐
相关产品推荐

