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

C#控制器中如何等待对象加入列表以返回API响应

问题描述

我现有一个Web API系统,需要向远程设备发送指令,等待其响应后返回给请求方。当前逻辑为生成UUID,远程响应匹配UUID后加入响应列表。请问能否await列表包含指定UUID?若不可行,如何跨类和线程将该对象传递至控制器以返回响应?

现有Web请求代码

[HttpGet]
[Route("Areas")]
public async Task<IHttpActionResult> GetArea(GetAreas getAreas)
{
    OutScaleManager.LogRequest("Request to getAreas with ip: " + getAreas.IP + " i " + getAreas.startArea + " i " + getAreas.endArea);
    foreach (NoxApiService noxService in OutScaleManager.nUtil.connectedInstance)
    {
        if (noxService.param.IPAddress == getAreas.IP)
        {
            noxService.GetAreas(getAreas.startArea, getAreas.endArea);
            OutScaleManager.LogRequest("getAreas Sent, now moving to queue checks");
            string UUID = OutScaleManager.nRAR.NoxRequestHandler(getAreas, RequestType.GETAREAS);
            if (OutScaleManager.nRAR.GetAreasResponseSend)
            {
                await OutScaleManager.nRAR.ResponseList.ContainsKey(UUID);
            }
            //if (OutScaleManager.nRAR.GetAreasResponseSend) { return Ok(OutScaleManager.nRAR.NoxRequestHandler(getAreas, RequestType.GETAREAS)); }
        }
    }
    OutScaleManager.LogRequest("This request was not sent, please create an instance and connected through the propper channels.");
    return Ok("This request was not sent, please create an instance and connected through the propper channels.");
}
解决方案

1. 不能直接await ContainsKey

ResponseList.ContainsKey(UUID)是同步方法,返回bool类型,并非可等待的Task,所以你写的await OutScaleManager.nRAR.ResponseList.ContainsKey(UUID)会直接编译报错,这种写法完全不可行。

2. 正确异步等待实现:使用TaskCompletionSource

要实现等待远程响应的异步逻辑,最可靠的方式是用TaskCompletionSource<T>——它能创建一个可等待的Task,当远程响应返回时,完成这个Task,控制器就能拿到结果返回给请求方。

具体实现步骤

  • 维护请求跟踪容器:在NoxRequestResponseHandler(即代码中的nRAR)中,用Dictionary<string, TaskCompletionSource<你的响应类型>>存储每个UUID对应的等待任务源,替代原有的ResponseList。
  • 发送请求时创建任务源:生成UUID后,创建TaskCompletionSource并存入字典,将其Task返回给控制器进行await。
  • 响应到达时完成任务:当远程设备返回匹配UUID的响应时,从字典中取出对应的TaskCompletionSource,调用SetResult传入响应数据,控制器的await会自动完成并返回结果。

修改后核心代码示例

改造NoxRequestResponseHandler类

public class NoxRequestResponseHandler
{
    // 用线程安全字典存储待处理请求的任务源
    private readonly ConcurrentDictionary<string, TaskCompletionSource<GetAreasResponse>> _pendingRequests = 
        new ConcurrentDictionary<string, TaskCompletionSource<GetAreasResponse>>();

    // 注册请求,返回可等待的Task和对应的UUID
    public (string Uuid, Task<GetAreasResponse> ResponseTask) RegisterGetAreasRequest(GetAreas request)
    {
        string uuid = Guid.NewGuid().ToString();
        var tcs = new TaskCompletionSource<GetAreasResponse>();
        _pendingRequests.TryAdd(uuid, tcs);
        
        return (uuid, tcs.Task);
    }

    // 远程响应到达时调用此方法完成任务
    public void CompleteGetAreasRequest(string uuid, GetAreasResponse response)
    {
        if (_pendingRequests.TryRemove(uuid, out var tcs))
        {
            tcs.SetResult(response);
        }
    }

    // 超时处理:避免请求无限挂起
    public void CancelRequest(string uuid)
    {
        if (_pendingRequests.TryRemove(uuid, out var tcs))
        {
            tcs.SetCanceled();
        }
    }
}

修改控制器方法

[HttpGet]
[Route("Areas")]
public async Task<IHttpActionResult> GetArea(GetAreas getAreas)
{
    OutScaleManager.LogRequest("Request to getAreas with ip: " + getAreas.IP + " i " + getAreas.startArea + " i " + getAreas.endArea);
    
    foreach (NoxApiService noxService in OutScaleManager.nUtil.connectedInstance)
    {
        if (noxService.param.IPAddress == getAreas.IP)
        {
            // 注册请求,获取UUID和可等待的Task
            var (uuid, responseTask) = OutScaleManager.nRAR.RegisterGetAreasRequest(getAreas);
            
            // 发送指令给远程设备(需携带UUID,让响应带回匹配)
            noxService.GetAreas(uuid, getAreas.startArea, getAreas.endArea);
            
            OutScaleManager.LogRequest("getAreas Sent, waiting for response");
            
            // 等待响应,同时添加超时处理(比如30秒)
            var completedTask = await Task.WhenAny(responseTask, Task.Delay(30000));
            if (completedTask == responseTask)
            {
                // 拿到响应返回给请求方
                var response = await responseTask;
                return Ok(response);
            }
            else
            {
                // 超时取消请求并返回提示
                OutScaleManager.nRAR.CancelRequest(uuid);
                return StatusCode(HttpStatusCode.RequestTimeout, "Request timed out waiting for device response");
            }
        }
    }
    
    OutScaleManager.LogRequest("This request was not sent, please create an instance and connected through the proper channels.");
    return Ok("This request was not sent, please create an instance and connected through the proper channels.");
}

关键注意事项

  • 线程安全:使用ConcurrentDictionary替代普通Dictionary,避免多线程访问时的冲突问题。
  • 超时机制:必须添加超时处理,防止请求因设备无响应而长时间挂起,占用服务器资源。
  • 资源清理:响应完成或超时后,从字典中移除对应的TaskCompletionSource,避免内存泄漏。

内容的提问来源于stack exchange,提问作者xguysOG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 13:50:47