2017-08-28 71 views
9

我有一个简单的同步方法,看起来像这样:异步返回集合(用C#7的功能)的各种方法

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); 
    } 
} 

由于该方法是一个Web应用程序的一部分,这是很好的利用一切资源尽可能有效。所以我想将方法​​改成异步之一。最简单的办法是做这样的事情:

public async Task<IEnumerable<Foo>> MyMethodAsync(Source src) 
{ 
    var oofs = await src.LoadOofsAsync(); 
    return oofs.Select(oof => Transform(oof)); 
} 

我不是在任async/awaitIEnumerable专家。然而,据我所知,使用这种方法“杀死”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); 
    } 
} 

我得到一个编译错误,那IObservable<T>确实实现既不GetEnumerator(),也不GetAwaiter() - 因此不能同时使用yieldasync。我还没有更深入地阅读Rx.NET的文档,所以我可能只是错误地使用了这个库。但我不想花时间学习一个新框架来修改单一方法。

使用新的possibilities in C# 7现在可以实现自定义类型。因此,理论上,我可以执行IAsyncEnumerable,这将定义GetEnumerator()GetAwaiter()方法。然而,从我以前的经验来看,我记得尝试创建一个自定义的GetEnumerator() ......我最终得到了一个隐藏在容器中的简单列表。

因此,我们要解决的任务4种可能的方法:

  1. 保持代码的同步,但与IEnumerable
  2. 将其更改为异步,但在Task<T>
  3. 包裹IEnumerable学习和使用的Rx .NET(System.Reactive)
  4. 使用C#7功能创建自定义IAsyncEnumerable

每种尝试的优点和缺点是什么?其中哪些对资源利用率影响最大?

+0

还有一个很容易的第5方式:https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq:'进行AsParallel()' –

+0

@ AdrianoRepetti AsParallel是否适合I/O绑定任务? – MickyD

+3

如果你担心“杀死”的好处,你真正应该担心的是'src.LoadOofsAsync()' - 那*已经*返回一个'Task >'。除此之外包装一些不同步是不会有帮助的,除非'Transform'实际上是昂贵的并且可以有意义地被并行化/等待。 –

回答

2
  • 保持代码的同步,但与IEnumerable的
  • 将其更改为异步,但在任务
  • 包裹IEnumerable的学习和使用Rx.NET(系统。无功)
  • 创建自定义IAsyncEnumerable用C#7特点

什么是这些尝试的优点和缺点?其中 对资源利用率影响最大?

在你的情况下,听起来最好的选择是Task<IEnumerable<T>>。下面是其中每个选项擅长什么:当没有I/O,但沉重的CPU使用

  1. 同步码(或平行同步码)的过人之处。如果你的I/O代码是同步等待的(就像你的第一个方法实现一样),CPU只是在等待Web服务响应而不做任何事情的时候正在循环。

  2. Task<IEnumerable<T>>适用于存在I/O操作以获取集合的情况。正在等待I/O操作的线程可以在等待时安排其他内容。 这听起来像你的情况。

  3. Rx最适合推送场景:哪里有数据被“推送”到您想要响应的代码。常见示例是接收股票市场定价数据或聊天应用程序的应用程序。

  4. IAsyncEnumerable适用于具有每个项目需要或生成异步任务的集合。示例:对项目集合进行迭代,并为每个项目执行某种唯一的数据库查询。如果你的Transform实际上是一个I/O绑定的异步方法,那么这可能更明智。