异步返回集合的各种方法(使用C#7功能)

时间:2017-08-28 14:27:21

标签: c# asynchronous .net-core system.reactive

我有一个简单的同步方法,如下所示:

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集合的“懒惰”。 / p>

在其他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()的自定义实现的尝试失败...我最终得到了一个隐藏在容器中的简单List。

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

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

    每次尝试的好处和缺点是什么?哪一项对资源利用率影响最大?

1 个答案:

答案 0 :(得分:2)

  
      
  • 保持代码同步,但使用IEnumerable
  •   
  • 将其更改为异步,但将IEnumerable包装在Task
  • 中   
  • 学习和使用Rx.NET(System.Reactive)
  •   
  • 使用C#7功能创建自定义IAsyncEnumerable
  •   
     

每次尝试的好处和缺点是什么?哪一个   它们对资源利用的影响最大?

在您的情况下,听起来最好的选择是Task<IEnumerable<T>>。以下是每个选项擅长的地方:

  1. 同步代码(或并行同步代码)在没有I / O但CPU使用率很高时表现优异。如果您的I / O代码同步等待(就像您的第一个方法实现一样),那么CPU只是在等待Web服务无所事事的情况下刻录循环。

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

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

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