在我的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>
是任务的枚举。
我想说,IAsynEnumerable
是IEnumerable<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
}
}