C# 如何使用Task.wheny并实施重试

C# 如何使用Task.wheny并实施重试,c#,async-await,C#,Async Await,我有一个解决方案,可以创建多个基于I/O的任务,我正在使用Task.WhenAny()来管理这些任务。但许多任务通常会由于网络问题或请求限制等原因而失败 在使用Task.WhenAny()方法时,我似乎找不到一个能够使我成功重试失败任务的解决方案 以下是我正在做的: var tasks = new List<Task<MyType>>(); foreach(var item in someCollection) { task.Add(GetSomethingAsyn

我有一个解决方案,可以创建多个基于I/O的任务,我正在使用Task.WhenAny()来管理这些任务。但许多任务通常会由于网络问题或请求限制等原因而失败

在使用Task.WhenAny()方法时,我似乎找不到一个能够使我成功重试失败任务的解决方案

以下是我正在做的:

var tasks = new List<Task<MyType>>();
foreach(var item in someCollection)
{
   task.Add(GetSomethingAsync());
}
while (tasks.Count > 0)
{
   var child = await Task.WhenAny(tasks);
   tasks.Remove(child);
   ???
}
var tasks=newlist();
foreach(someCollection中的变量项)
{
Add(GetSomethingAsync());
}
而(tasks.Count>0)
{
var child=wait Task.WhenAny(任务);
任务。删除(子项);
???
}

因此,上面的结构适用于完成任务,但我还没有找到处理和重试失败任务的方法。
等待任务。当任何
抛出聚合异常而不允许我检查任务状态时。在异常处理程序中,我不再有任何方法重试失败的任务

我相信在任务中重试,然后用
任务替换
任务。WhenAny
-in-a-loop反模式会更容易。WhenAll

例如,使用:


如果出于某种原因不想使用库,可以使用下面的
Retry
方法。它接受一个任务工厂,并继续创建任务,然后等待任务,直到任务成功完成,或者到达
maxtures

public static async Task<TResult> Retry<TResult>(Func<Task<TResult>> taskFactory,
    int maxAttempts)
{
    int failedAttempts = 0;
    while (true)
    {
        try
        {
            var task = taskFactory();
            return await task.ConfigureAwait(false);
        }
        catch
        {
            failedAttempts++;
            if (failedAttempts >= maxAttempts) throw;
        }
    }
}
输出:

Url:112276个字符
Url:,122784个字符

如果取消对第三个url的注释,则在五次尝试失败后,将抛出一个
HttpRequestException
,而不是这些结果


该方法将在传播错误之前等待所有任务的完成。如果最好尽快报告错误,您可以找到有问题的解决方案。

您能否澄清任务之间的区别?目前,您似乎有一个相同任务的列表。也许你正在传递给
GetSomethingAsync()
一些不同的参数?@Sergey,是的,我正在传递一个唯一的参数给GetSomethingAsync,我需要在重试任务时能够引用它。谢谢@Stephen,我会尝试一下。我和Polly在这方面做了些尝试,但仍然在使用等待任务。当任何一个模式都不起作用时。@macon:不,只是很尴尬。总有比使用
任务更干净的解决方案。当任何
任务完成时删除任务(最终等待它们全部完成)。对于传播所有异常的更复杂的实现,请查看。
var policy = ...; // See Polly documentation
var tasks = someCollection.Select(item => policy.ExecuteAsync(() => GetSomethingAsync()));
await Task.WhenAll(tasks);
public static async Task<TResult> Retry<TResult>(Func<Task<TResult>> taskFactory,
    int maxAttempts)
{
    int failedAttempts = 0;
    while (true)
    {
        try
        {
            var task = taskFactory();
            return await task.ConfigureAwait(false);
        }
        catch
        {
            failedAttempts++;
            if (failedAttempts >= maxAttempts) throw;
        }
    }
}
string[] urls =
{
    "https://stackoverflow.com",
    "https://superuser.com",
    //"https://no-such.url",
};
var httpClient = new HttpClient();
var tasks = urls.Select(url => Retry(async () =>
{
    return (Url: url, Html: await httpClient.GetStringAsync(url));
}, maxAttempts: 5));

var results = await Task.WhenAll(tasks);
foreach (var result in results)
{
    Console.WriteLine($"Url: {result.Url}, {result.Html.Length:#,0} chars");
}