[英]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()
的那一刻,客户端等待服务器返回数据。 但这无法预测服务器正在返回哪些数据。 所以会造成死锁。
我需要某种IsDataAvailableOnChannel
但AsyncDuplexStreamingCall
上不存在这种方法
您可以将数据发送交给另一个任务,然后在完成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.