我使用TPL抓取一组Urls,然后进行一些处理。
for (int i = 0; i < list.Count; i++)
{
var tuple = list[i];
string url = tuple.Item2;
tasks[i] = httpClient.GetStringAsync(url).
ContinueWith(task => {
{
......
});
}
Task.WaitAll(tasks);
问题是,在Task.WaitAll
语句中,它似乎经常会抛出异常,因为任务已被取消。我理解httpClient.GetStringAsync
可能并不总能确保成功,因此我希望在发生异常时在httpClient.GetStringAsync
中添加重试逻辑。这样做的正确方法是什么?
答案 0 :(得分:2)
您可以使用GetStringAsync
循环轻松地围绕for
重试,直到没有异常或达到重试限制为止。我存储任务并使用await
从中提取结果,因此如果达到重试限制但未成功,则会重新抛出异常:
async Task<string> GetStringAsync(HttpClient client,string url, int retries)
{
Task<string> task = null;
for (int i = 0; i < retries; i++)
{
try
{
task = client.GetStringAsync(url);
await task;
break;
}
catch
{
// log
}
}
return await task;
}
您甚至可以将其作为HttpClient
上的扩展方法:
static async Task<string> GetStringAsync(this HttpClient client, string url, int retries);
答案 1 :(得分:0)
如果您不想使用async/await
,可以使用以下扩展方法作为起点。
static class HttpClientExtentions
{
public static Task<string> GetStringWithRetryAsync(this HttpClient client, string url, int retries)
{
var completion = new TaskCompletionSource<string>();
var ex = new List<Exception>();
Task<string> getString = client.GetStringAsync(url);
Action<Task<string>> continueAction = null;
continueAction = (t) =>
{
if (t.IsFaulted)
{
ex.Add(t.Exception);
if (retries-- > 0)
{
getString = client.GetStringAsync(url);
getString.ContinueWith(continueAction);
}
else
{
completion.SetException(new AggregateException(ex));
}
}
else // assume success, you could also handle cancellation
{
completion.SetResult(t.Result);
}
};
getString.ContinueWith(continueAction);
return completion.Task;
}
}
以这种方式使用:
for (int i = 0; i < list.Count; i++)
{
var tuple = list[i];
string url = tuple.Item2;
int retryCount = 3;
var httpClient = new HttpClient(); // should create new object for each req
tasks[i] = httpClient.GetStringWithRetryAsync(url, retryCount).
ContinueWith(task => {
{
//......
});
}
Task.WaitAll(tasks);