繁体   English   中英

使用异步枚举同时在 .NET 中写入和读取 gRPC 全双工通道

[英]Writing and reading to/from gRPC full duplex channel in .NET simultaneously using async enumerables

我有一个客户端-服务器场景,其中管道完全使用 gRPC 双工流实现。 管道由客户端启动。 在每一步(双工调用),客户端从 IAsyncEnumerable 读取项目并将它们写入通道。 这些项目由服务器处理,其中一些在通道上异步发送回。 然后客户端返回带有产量的读取项目。 我有 4 种方法像这样被链接起来。

我使用的模式:

public async IAsyncEnumerable<Result> Results(IAsyncEnumerable<Input> inputs)
{
    var duplex = Server.GetResults();

    await foreach (var input in inputs)
    {
        await duplex.RequestStream.WriteAsync(input);
    }

    await duplex.RequestStream.CompleteAsync();

    await foreach (var result in duplex.ResponseStream.ToAsyncEnumerable())
    {
        yield return result;
    }
}

这种模式的问题是服务器在第二次 foreach(从通道读取)开始之前将数据发回。如果有很多数据,则通道缓冲区在读取开始之前(可能)会变满并且操作被取消gRPC 抛出异常。

如何在不破坏 IAsyncEnumerables 和产量的情况下重构它? 如果这是不可能的,那么我还能如何实现“先写,后读”的场景?

只是为了澄清,我不能使用以下模式:

while(await stream.MoveNext()) 
{
   // send
   // yield
}

在我调用await duplex.ResponseStream.MoveNext()的那一刻,客户端等待服务器返回数据。 但这无法预测服务器正在返回哪些数据。 所以会造成死锁。

我需要某种IsDataAvailableOnChannelAsyncDuplexStreamingCall上不存在这种方法

您可以将数据发送交给另一个任务,然后在完成yield后等待它。

假设我假设您有一个普通的 ThreadPool 调度程序,每个函数的await应该是交错的。

public async IAsyncEnumerable<Result> Results(IAsyncEnumerable<Input> inputs)
{
    var duplex = Server.GetResults();

    var sendingTask = Task.Run(async () =>
    {
        await foreach (var input in inputs)
        {
            await duplex.RequestStream.WriteAsync(input);
        }

        await duplex.RequestStream.CompleteAsync();
    });

    await foreach (var result in duplex.ResponseStream.ToAsyncEnumerable())
    {
        yield return result;
    }

    await sendingTask;
}

如果将匿名 lambda 放入适当的函数中,可能会更容易管理。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM