ASP.NET Web API微服务:等待任务完成失败问题求助
问题根源与解决方案
你的问题核心出在SessionState的CompleteTask实现完全错误,它每次被调用都会新建一个TaskCompletionSource,并且立刻调用SetResult(true)——等于这个方法返回的Task永远是已完成状态。不管EventProcessor有没有处理消息,只要你调用sessionState.CompleteTask(),任务就直接结束了,这就是Task.WhenAny会提前触发的原因。
另外还有两个次要错误:
ProductQuantityEvaluation方法里漏写了await,就算逻辑正确,Task.WhenAny也不会被等待,方法会直接返回。- 单例的
SessionState如果同时处理多个请求,还会出现任务被其他请求覆盖的问题(先解决核心异步等待问题,后面会提这个点)。
第一步:修复SessionState的实现
把TaskCompletionSource作为类的成员变量,拆分出“获取等待任务”和“触发任务完成”两个独立方法:
public class SessionState { // 单例下需注意多请求隔离,后续可结合correlationId优化 private TaskCompletionSource<bool> _quantityCheckTcs; // 初始化并返回要等待的任务(需在发送库存查询消息前调用) public Task GetQuantityCheckTask() { _quantityCheckTcs = new TaskCompletionSource<bool>(); return _quantityCheckTcs.Task; } // 触发任务完成,由EventProcessor处理完消息后调用 public void CompleteQuantityCheck() { _quantityCheckTcs?.SetResult(true); } }
第二步:修改CheckoutShoppingCart的结算逻辑
在发送库存查询消息前先初始化等待任务,然后等待该任务完成后再继续支付流程:
public class CheckoutShoppingCart { public async Task ProcessCheckout(Cart loggedUserCart) { // 1. 初始化库存检查的等待任务 var quantityCheckTask = sessionState.GetQuantityCheckTask(); // 2. 发送查询库存的消息给目录API await PublishMessageAvailableQuantities(loggedUserCart.Products); // 3. 等待目录API的回复被处理完成 await quantityCheckTask; // 4. 继续执行支付流程 await ProcessPayment(); } }
第三步:修改EventProcessor的消息处理逻辑
调用SessionState的CompleteQuantityCheck方法来触发等待任务完成:
public class EventProcessor { public async Task ProcessMessage(string message, string eventType) { switch(eventType) { case AppConstants.eventTypeSendActualProductQuantities: var disapprovedProductInfo = JsonSerializer.Deserialize<PublishedProductModel>(message); sessionState.publishedProductModel = disapprovedProductInfo; sessionState.productQuantityStatus = 2; // 触发等待任务完成,无需await(同步操作) sessionState.CompleteQuantityCheck(); break; // 其他消息类型处理... } } }
额外注意事项
- 多请求隔离问题:因为
SessionState是单例,上面的基础实现会导致多个用户的结算请求互相干扰。解决这个问题必须用correlationId——发送库存查询消息时生成唯一的correlationId,将其存入消息中;SessionState维护一个Dictionary<string, TaskCompletionSource<bool>>,用correlationId映射每个请求的任务;EventProcessor处理回复消息时带回correlationId,找到对应的TaskCompletionSource触发完成。这其实就是RabbitMQ官方推荐的请求-响应模式,是这类场景的标准解决方案。 - 超时处理:添加超时逻辑避免消息丢失导致任务永远等待,比如用
await Task.WhenAny(quantityCheckTask, Task.Delay(10000)),判断如果是超时任务先完成,则抛出异常或执行错误处理逻辑。
内容的提问来源于stack exchange,提问作者Andrеw
相关产品推荐
相关产品推荐

