.NET 6 API中如何实现外部共享资源的独占锁定?
解决.NET 6 API中外部共享资源的并发竞态问题
由于你的API基于async-await异步架构,不能使用传统lock关键字(会阻塞线程池线程、破坏异步模型),需根据部署架构选择适配的异步锁方案:
单进程部署场景(单API实例)
使用SemaphoreSlim实现异步互斥锁,它支持异步等待,不会阻塞线程,适合单进程内的并发控制。
实现代码
// 全局静态锁实例,初始计数为1表示同时仅允许1个请求进入临界区 private static readonly SemaphoreSlim _sharedResourceLock = new SemaphoreSlim(1, 1); [HttpPost] public async Task<IActionResult> CreateResourcesAndModifyShared() { // 异步等待获取锁 await _sharedResourceLock.WaitAsync(); try { // 临界区:修改外部共享资源 + 创建独立资源的逻辑 await ModifyExternalSharedResourceAsync(); await CreateIndependentResourcesAsync(); } finally { // 无论是否发生异常,必须释放锁,避免死锁 _sharedResourceLock.Release(); } return Ok("操作完成"); }
关键注意事项
- 必须用
try-finally包裹临界区逻辑,确保锁始终被释放 - 使用
WaitAsync()而非Wait(),适配异步架构,避免阻塞线程池 - 锁实例需设为静态,保证跨请求共享同一个锁对象
多进程部署场景(负载均衡/多API实例)
单进程锁无法跨实例生效,需使用分布式锁协调多个API实例的并发请求。以下以Redis RedLock为例(主流分布式锁实现):
实现步骤
- 安装NuGet包:
RedLock.net、StackExchange.Redis - 配置并使用分布式锁:
// 初始化RedLock工厂(建议注册为单例服务) private readonly IRedLockFactory _redLockFactory; public YourController(IRedLockFactory redLockFactory) { _redLockFactory = redLockFactory; } [HttpPost] public async Task<IActionResult> CreateResourcesAndModifyShared() { // 尝试获取锁:锁名称(唯一标识共享资源)、等待超时、自动释放时间 using var redLock = await _redLockFactory.CreateLockAsync( resource: "external-shared-resource-lock", expiryTime: TimeSpan.FromSeconds(15), // 锁自动释放时间,需大于临界区操作耗时 waitTime: TimeSpan.FromSeconds(5), // 等待获取锁的最长时间 retryTime: TimeSpan.FromMilliseconds(500) // 重试间隔 ); if (!redLock.IsAcquired) { // 获取锁失败,返回繁忙状态让客户端重试 return StatusCode(503, "资源繁忙,请稍后重试"); } // 临界区逻辑 await ModifyExternalSharedResourceAsync(); await CreateIndependentResourcesAsync(); return Ok("操作完成"); }
关键注意事项
- 锁的自动释放时间必须大于临界区操作的最大耗时,避免锁提前释放导致竞态
- 需处理获取锁失败的场景,返回合适的HTTP状态码引导客户端重试
- 分布式锁依赖外部存储(如Redis、SQL Server),需保证存储服务的可用性
禁用方案提醒
绝对不要使用lock关键字处理异步代码中的并发,lock会阻塞线程,而async-await会释放线程上下文,可能导致锁被错误持有或死锁。
内容的提问来源于stack exchange,提问作者Sannix19
相关产品推荐
相关产品推荐

