.NET WebAPI中Task.Run封装同步数据库方法的超时与取消实现问题
问题1 超时逻辑实现
可以通过CancellationTokenSource的超时构造函数实现60秒超时控制,触发超时后会抛出OperationCanceledException,可直接在现有异常逻辑中捕获处理。
注意:由于你的数据库操作是同步且不可修改的,超时后仅会让当前请求提前返回响应,后台的同步数据库操作仍会执行到结束,不推荐强行终止线程,否则可能导致不可预知的资源泄漏问题。
问题2 用户主动取消适配
ASP.NET Core控制器的Action可直接声明CancellationToken类型的参数,框架会自动将用户断开请求对应的RequestAborted令牌绑定到该参数上。你可以把这个令牌和超时用的令牌通过LinkedTokenSource合并,两种场景任意一种触发都会生效。
修改后的代码示例
[HttpPost] [Route("insert")] // 新增CancellationToken参数,框架自动绑定请求取消令牌 public async Task<IActionResult> Insert(Customer customer, CancellationToken requestAborted) { customer.InsertUser = string.IsNullOrWhiteSpace(_currentUserService.UserId) ? "api" : _currentUserService.UserId; try { // 构造60秒超时的令牌源 using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(60)); // 合并超时令牌和请求取消令牌 using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(timeoutCts.Token, requestAborted); using (CustomerBC customerBC = new()) { var task = Task.Run(() => customerBC.NewItem(customer), linkedCts.Token); await task; } return Ok(await Result<Customer>.SuccessAsync(customer)); } catch (OperationCanceledException) { List<string> messages = new() { "请求已超时或用户主动取消,请稍后重试" }; return Ok(Result<Customer>.FailAsync(messages)); } catch(Exception ex) { List<string> messages = new() { string.Format("Error: {0},Source : {1}", ex.Message, ex.Source) }; return Ok(Result<Customer>.FailAsync(messages)); } }
补充说明
- 如果需要在取消/超时的时候尽可能释放数据库相关资源,可以在捕获到
OperationCanceledException之后手动调用customerBC的释放方法(如果提供的话),前提是你确认该方法是线程安全的。 - 同步方法包裹
Task.Run的方案会额外占用一个线程池线程,高并发场景下如果数据库操作耗时普遍较长,可能会出现线程池饥饿的问题,后续有改造空间时优先把底层数据库操作改成异步实现。
内容的提问来源于stack exchange,提问作者Alex Ivan
相关产品推荐
相关产品推荐

