lss*_*lss 9 c# asynchronous system.reactive .net-core
我有一个简单的同步方法,看起来像这样:
public IEnumerable<Foo> MyMethod(Source src)
{
// returns a List of Oof objects from a web service
var oofs = src.LoadOofsAsync().Result;
foreach(var oof in oofs)
{
// transforms an Oof object to a Foo object
yield return Transform(oof);
}
}
Run Code Online (Sandbox Code Playgroud)
由于该方法是Web应用程序的一部分,因此尽可能有效地使用所有资源是很好的.因此,我想将方法更改为异步方法.最简单的选择是做这样的事情:
public async Task<IEnumerable<Foo>> MyMethodAsync(Source src)
{
var oofs = await src.LoadOofsAsync();
return oofs.Select(oof => Transform(oof));
}
Run Code Online (Sandbox Code Playgroud)
我不是专家要么async/ await或IEnumerable.但是,从我的理解,使用这种方法"杀死"的好处IEnumerable,因为等待任务,直到整个集合被加载,从而省略了IEnumerable集合的"懒惰" .
在其他StackOverflow帖子上,我已经阅读了几个使用Rx.NET(或System.Reactive)的建议.快速浏览我读过的文档,这IObservable<T>是他们的异步替代品IEnumerable<T>.但是,使用天真的方法并尝试键入以下内容不起作用:
public async IObservable<Foo> MyMethodReactive(Source src)
{
var oofs = await src.LoadOofsAsync();
foreach(var oof in oofs)
{
yield return Transform(oof);
}
}
Run Code Online (Sandbox Code Playgroud)
我得到了一个编译错误,它IObservable<T>确实没有实现GetEnumerator(),也没有GetAwaiter()- 因此它不能同时使用yield和async.我没有更深入地阅读Rx.NET的文档,所以我可能只是错误地使用了库.但我不想花时间学习一个新框架来修改单个方法.
凭借C#7中的新功能,现在可以实现自定义类型.因此,理论上,我可以实现一个IAsyncEnumerable定义两者GetEnumerator()和GetAwaiter()方法的方法.但是,根据我以前的经验,我记得创建自定义实现的尝试失败GetEnumerator()...我最终得到了一个隐藏在容器中的简单List.
因此,我们有4种可能的方法来解决任务:
IEnumerableIEnumerable为Task<T>IAsyncEnumerable使用C#7功能创建自定义每次尝试的好处和缺点是什么?哪些对资源利用影响最大?
- 保持代码同步,但使用 IEnumerable
- 将其更改为异步,但将 IEnumerable 包装在任务中
- 学习和使用 Rx.NET (System.Reactive)
- 使用 C# 7 功能创建自定义 IAsyncEnumerable
这些尝试各自的优点和缺点是什么?其中哪一个对资源利用率影响最显着?
根据您的情况,听起来最好的选择是Task<IEnumerable<T>>。以下是每个选项的优点:
当没有 I/O 但 CPU 使用率很高时,同步代码(或并行同步代码)表现出色。如果您有同步等待的 I/O 代码(如您的第一个方法实现),CPU 只是在等待 Web 服务响应时消耗周期而不执行任何操作。
Task<IEnumerable<T>>适用于有 I/O 操作来获取集合的情况。然后,运行等待 I/O 操作的线程可以在等待时安排其他事情。这听起来像你的情况。
Rx 最适合推送场景:数据被“推送”到您想要响应的代码。常见的示例是接收股票市场定价数据的应用程序或聊天应用程序。
IAsyncEnumerable适用于当您有一个集合,其中每个项目都需要或生成异步任务时。一个示例:迭代项目集合并为每个项目执行某种唯一的数据库查询。如果您Transform实际上是一个 I/O 绑定的异步方法,那么这可能更明智。