为什么AsyncUnaryCall<T>等类没有继承Task<T>?

11
在使用 C# 中的 gRPC 时,异步调用会返回 AsyncUnaryCall<T>(对于一元调用,其他调用类型的返回类型稍有不同)。然而,AsyncUnaryCall<T> 并未扩展 Task<T>。因此,您通常会使用 Task<T> 完成的常见操作无法使用 AsyncUnaryCall<T> 完成。这些操作包括:
  • 指定续约策略(使用 ConfigureAwait)
  • 使用像 Task.WhenAny 和 Task.WhenAll 这样的辅助方法
后者正在困扰着我,因为我想启动多个 gRPC 调用并等待它们全部完成。目前似乎我的唯一选择是编写一个小帮助程序,一个接一个地等待其完成。
为什么 AsyncUnaryCall<T> 没有模仿 Task<T> 中的功能呢?

1
它看起来像是为其头部和完整响应分别公开了Task(请参见ResponseAsyncResponseHeadersAsync)。为什么这些不足够呢? - Damien_The_Unbeliever
2
你并不一定需要使用 Task 才能使用 await,类型只需实现 awaitable/awaiter 模式即可,而 AsyncUnaryCall 就是这样的类型。 - Dmytro Mukalov
您可以传递 call.ResponseAsync。或者,对于多个调用:await Task.WhenAll(calls.Select(x => x.ResponseAsync)); - Stephen Cleary
不错,我在查看 API 时不知道怎么漏掉了。谢谢。 - me--
有一件事仍然不清楚,那就是我是否应该为每个调用取消引用 ResponseAsync,以便我可以在其上使用 ConfigureAwait(false) - me--
1个回答

14
如我在评论中所说,虽然它看起来像是“任务”,但实际上它代表两个独立的Task。如果你想要像Task一样处理单个Task,只需访问相应的属性(例如ResponseHeadersAsyncResponseAsync)。
如果你有一个类型为List<AsyncUnaryCall<T>>的变量themAll,那么使用WhenAll/WhenAny就很容易:
await Task.WhenAny(themAll.Select(c=>c.ResponseHeadersAsync));

如果您在任何标头到达时都可以执行有用的工作,或者

await Task.WhenAll(themAll.Select(c=>c.ResponseAsync));

如果在所有任务完成之前您无法执行任何有用的操作,则可以使用其中一个示例。同样,如果您想这样做,可以提取其中一个任务并在ConfigureAwait中使用await


我对探索众多gRPC流的 WhenAny 很感兴趣。有没有可靠的示例,在一个线程中处理多个流,无论哪个流产生数据都会尽快处理?此外,在从 Task.WhenAny(...) 返回后,其他流的状态会发生什么? - Aaron Hudon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接