我应该选择IAsyncEnumerable<T>还是IEnumerable<Task<T>>?



在我的c#程序中,我有一个包含100多个url的列表,我将从这些url下载文件。

首先,我将使用list.select(x=> DosomethingAsync(x)),然后使用Task.WhenAny来处理结果(这一步非常快,主要成本是文件下载)

while (!itemsTasks.IsNullOrEmpty())
{
Task<Stream> fininshed_item_task = await Task.WhenAny(itemsTasks);
Stream itemResult = await fininshed_item_task;

// do something with   itemResult
......
itemsTasks.Remove(fininshed_item_task);
}

现在我听说有IAsyncEnumerable,它似乎可以保持我的代码更直接。

但我非常担心IAsyncEnumerable将逐个获取文件并花费更多时间。我发现IAsyncEnumerable做了很多线程更改!

IEnumerable<Task<T>>似乎在等待之前立即启动任务。另一个问题是,如果有一个get失败,我需要找到一种方法来取消剩余的任务。

你能解释一下吗?

基本上IAsyncEnumerable是等待任务结果的枚举,而IEnumerable<Task>是任务的枚举。

我想说,IAsynEnumerableIEnumerable<Task<T>>的一个特定用例的快捷功能。

假设我们有一个昂贵的操作要执行

static async Task<int> ExpensiveOperationAsync(int number)
{
// some expensive operation
await Task.Delay(100);
return number;
}

要返回对所有正数的操作结果,显然不能只返回值列表,因为它们的数量是无限的,因此必须在任务完成后返回每个结果。

但是这不能编译,因为int不是可等待的,方法是async

static async int GetNumbersAsync()
{
var i = 0;

while (true)
{
yield return await ExpensiveOperationAsync(i++);
}
}

一种方法是不使用aync方法,只返回任务,这里我们有IEnumerable<Task<int>>

static IEnumerable<Task<int>> GetNumbersAsyncEnumerableTasks()
{
var i = 0;

while (true)
{
yield return ExpensiveOperationAsync(i++);
}
}

并使用它

foreach (var task in GetNumbersAsyncEnumerableTasks())
Console.WriteLine(await task);

但是这种方法的一个缺点是它对你想要达到的目标不是很明确。

c#提供了一种更好的方法,IAsyncEnumerable<T>,这是一种可等待的东西。

static async IAsyncEnumerable<int> GetNumbersAsyncEnumerable(int max)
{
for (var i = 0; i < max + 1; i++)
{
yield return await ExpensiveOperationAsync(i);
}
}
// notice the await foreach syntax
await foreach (var number in GetNumbersAsyncEnumerable(10))
Console.WriteLine(number);

正如你所看到的,IAsynEnumerable<T>在一些特定的情况下是有用的,但它似乎不适合你的情况。

我宁愿这样做

public class MyProcessor
{
public async Task ProcessAllTasksAsync<T>(IEnumerable<Task<T>> tasks)
{
var wrappedTasks = tasks.Select(TaskWrapperAsync);
await Task.WhenAll(wrappedTasks);
}
private async Task TaskWrapperAsync<T>(Task<T> task)
{
var result = await task;
DoSomethingWithItemResult(result);
}
private void DoSomethingWithItemResult<T>(T result)
{
// some cool stuff
}
}

最新更新