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

gRPC服务端Task参数作用域问题:如何在外部及主线程使用请求参数

解决gRPC服务端请求参数跨实例/线程访问问题

你遇到的核心问题有两个:

  1. 实例不共享:StartServer方法里创建了新的LoggingImpl实例绑定到gRPC服务,而你外部调用GetRequestInfo的是另一个实例,两个实例的currentRequest完全独立,所以外部拿不到值。
  2. 线程安全风险:gRPC服务方法是并发执行的,多个客户端请求会同时修改currentRequest,直接赋值会导致数据覆盖或线程安全问题。

1. 修复实例不一致问题

修改StartServer方法,用当前实例绑定服务,而非创建新实例:

public void StartServer()
{
    server = new Server
    {
        Services = { Logging.BindService(this) }, // 使用当前实例,而非new LoggingImpl()
        Ports = { new ServerPort("127.0.0.1", 50010, ServerCredentials.Insecure) }
    };
    server.Start();
}

2. 线程安全的请求存储方案

根据业务需求选择合适的存储方式:

方案A:保存最新请求

用原子操作确保多线程下赋值/读取的安全性:

private RequestInfo _currentRequest;

public override Task<LoggingResponse> LoggingMCM(RequestInfo request, ServerCallContext context)
{
    // 原子操作更新最新请求
    Interlocked.Exchange(ref _currentRequest, request);
    Console.WriteLine(_currentRequest);
    return Task.FromResult(new LoggingResponse { Result = "this is a reply from Unity Server" });
}

public void GetRequestInfo()
{
    // 原子操作安全读取
    var request = Interlocked.CompareExchange(ref _currentRequest, null, null);
    Console.WriteLine(request);
}

方案B:保存所有请求

用线程安全集合存储所有请求记录:

private readonly ConcurrentQueue<RequestInfo> _allRequests = new ConcurrentQueue<RequestInfo>();

public override Task<LoggingResponse> LoggingMCM(RequestInfo request, ServerCallContext context)
{
    _allRequests.Enqueue(request);
    Console.WriteLine(request);
    return Task.FromResult(new LoggingResponse { Result = "this is a reply from Unity Server" });
}

// 示例:获取所有请求
public List<RequestInfo> GetAllRequests()
{
    return _allRequests.ToList();
}

3. 在其他类/主线程中访问请求

跨类访问

确保其他类持有同一个LoggingImpl实例的引用,直接调用对应的方法或访问属性:

// 比如在ClientController中注入LoggingImpl实例
public class ClientController
{
    private readonly LoggingImpl _loggingServer;

    public ClientController(LoggingImpl loggingServer)
    {
        _loggingServer = loggingServer;
    }

    public void ShowLatestRequest()
    {
        _loggingServer.GetRequestInfo();
    }
}

Unity主线程处理

如果是Unity环境,gRPC服务方法运行在后台线程,需要回到主线程处理请求数据,可以实现一个简易主线程调度器:

// 简易Unity主线程调度器
public static class MainThreadDispatcher
{
    private static readonly Queue<Action> _actions = new Queue<Action>();

    public static void Enqueue(Action action)
    {
        lock (_actions)
        {
            _actions.Enqueue(action);
        }
    }

    // 在Unity的Update方法中调用此方法
    public static void Update()
    {
        lock (_actions)
        {
            while (_actions.Count > 0)
            {
                _actions.Dequeue().Invoke();
            }
        }
    }
}

然后在gRPC方法中使用:

public override Task<LoggingResponse> LoggingMCM(RequestInfo request, ServerCallContext context)
{
    // 拷贝请求数据,避免后续被并发修改
    var requestCopy = new RequestInfo
    {
        // 复制所有需要的字段
        Id = request.Id,
        Message = request.Message
    };

    // 将处理逻辑放到主线程执行
    MainThreadDispatcher.Enqueue(() =>
    {
        Console.WriteLine(requestCopy);
        // 通知ClientController处理请求
        controller.OnNewRequestReceived(requestCopy);
    });

    return Task.FromResult(new LoggingResponse { Result = "this is a reply from Unity Server" });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 01:16:10