You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
            // 其他消息类型处理...
        }
    }
}

额外注意事项

  1. 多请求隔离问题:因为SessionState是单例,上面的基础实现会导致多个用户的结算请求互相干扰。解决这个问题必须用correlationId——发送库存查询消息时生成唯一的correlationId,将其存入消息中;SessionState维护一个Dictionary<string, TaskCompletionSource<bool>>,用correlationId映射每个请求的任务;EventProcessor处理回复消息时带回correlationId,找到对应的TaskCompletionSource触发完成。这其实就是RabbitMQ官方推荐的请求-响应模式,是这类场景的标准解决方案。
  2. 超时处理:添加超时逻辑避免消息丢失导致任务永远等待,比如用await Task.WhenAny(quantityCheckTask, Task.Delay(10000)),判断如果是超时任务先完成,则抛出异常或执行错误处理逻辑。

内容的提问来源于stack exchange,提问作者Andrеw

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 02:17:35