我有一个简单的同步方法,看起来像这样:异步返回集合(用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
/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);
}
}
我得到一个编译错误,那IObservable<T>
确实实现既不GetEnumerator()
,也不GetAwaiter()
- 因此不能同时使用yield
和async
。我还没有更深入地阅读Rx.NET的文档,所以我可能只是错误地使用了这个库。但我不想花时间学习一个新框架来修改单一方法。
使用新的possibilities in C# 7现在可以实现自定义类型。因此,理论上,我可以执行IAsyncEnumerable
,这将定义GetEnumerator()
和GetAwaiter()
方法。然而,从我以前的经验来看,我记得尝试创建一个自定义的GetEnumerator()
......我最终得到了一个隐藏在容器中的简单列表。
因此,我们要解决的任务4种可能的方法:
- 保持代码的同步,但与
IEnumerable
- 将其更改为异步,但在
Task<T>
- 包裹
IEnumerable
学习和使用的Rx .NET(System.Reactive) - 使用C#7功能创建自定义
IAsyncEnumerable
每种尝试的优点和缺点是什么?其中哪些对资源利用率影响最大?
还有一个很容易的第5方式:https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/introduction-to-plinq:'进行AsParallel()' –
@ AdrianoRepetti AsParallel是否适合I/O绑定任务? – MickyD
如果你担心“杀死”的好处,你真正应该担心的是'src.LoadOofsAsync()' - 那*已经*返回一个'Task>'。除此之外包装一些不同步是不会有帮助的,除非'Transform'实际上是昂贵的并且可以有意义地被并行化/等待。 –