2

我使用TPL來抓取一組Url,然後做一些處理。在Task.WaitAll中處理取消的任務和任務例外?

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

如果重試失敗,你還想要一個例外? – i3arnon 2014-10-10 23:13:12

+0

@ I3arnon:我知道。但是,這個異常將作爲AggregateException傳遞給Task.WaitAll。是對的嗎? – derekhh 2014-10-10 23:15:16

+0

是的,因爲任務可能包含幾個例外。如果你'等待爲'async-await'構建的Task.WhenAll',那麼你會得到實際的異常(第一個是)。 – i3arnon 2014-10-10 23:17:04

回答

2

您可以使用for循環輕鬆地在GetStringAsync周圍進行重試,該循環會嘗試執行,直到沒有異常或達到重試限制爲止。我存儲任務,並從它使用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); 
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);