2015-03-02 161 views
0

我有一個通用應用程序,可更新來自Internet源的多個對象。爲了更新進度指示器並防止重複請求,我有以下邏輯。異步/等待鏈掛起或死鎖

在刷新按鈕:

foreach (var package in Packages.Where(item => !item.Received)) 
{ 
    RefeshPackage(package); 
} 

其中在列表中呼籲RefreshPackage每個包:

private async Task RefeshPackage(PackageModel package) 
{ 
    if (Tasks.Contains(package.Id)) return;  
    Tasks.Add(package.Id); 

    await DownloadAndUpdate(package); 

    Tasks.Remove(package.Id); 
    Refresh.RaiseCanExecuteChanged(); 
} 

這對於每個包調用下載升級:

private async Task DownloadAndUpdate(PackageModel package) 
{ 
    var response = await webService.GetPackageStages(package.Id); 
    if (response != null) 
    { 
     switch (response.Status) 
     { 
      case 200: 
       package.UpdateStages(response.Stages); 
       break; 

      case 500: 
       //package doesn't exist or website down 
       break; 

      default: 
       break; 
     } 
    } 
    else 
    { 
     //no network/timed out 
    } 
} 

這對於要求GetPackageStages每個包裝:

public async Task<ResponseData> GetPackageStages(string id) 
{ 
    var requestUri = string.Concat(MyUri, id); 

    var client = new HttpClient(); 
    client.Timeout = new TimeSpan(0,0,5); 

    try 
    { 
     var response = await client.GetAsync(requestUri); 
     if(response.StatusCode == System.Net.HttpStatusCode.OK) 
     { 
      //process response 
     } 
    catch (Exception e) 
    { 
     return null; 
    } 
    finally 
    { 
     client.Dispose(); 
    } 
} 

懸/鎖死總是發生在最後一個函數,在該行:

await client.GetAsync(requestUri); 

有時它掛在這裏永遠有時它工作得很好。我已經閱讀了很多關於死鎖的其他解答,常見的解決方案是,使用異步/等待(我已經在做)或使用。配置等待(false)無處不在(我已經做了沒有成功)。

是否還有別的我失蹤或應該以不同的方式構建我的代碼?我已經用完了想法/解決方案。任何幫助,將不勝感激。

+0

'RefreshPackage'是一種異步方法。你爲什麼不對外部'foreach'循環中產生的'Task'做任何事情?即使你不想等待它,至少你應該觀察它的錯誤('.ContinueWith(,... TaskContinuationOptions.OnlyOnFaulted)')。 – 2015-03-02 12:21:52

+0

你會怎麼知道?你不等待'RefeshPackage(package)'的結果;' – Jodrell 2015-03-02 12:22:19

+0

它是掛起還是去//沒有網絡/超時情況? – gabba 2015-03-02 12:24:13

回答

0

外部的foreach只是簡單地執行RefeshPackage而沒有對結果做任何事情。這種「隨意忘記」方法的問題是異常將無法被發現,因此很難判斷異步代碼是否確實正確執行。即使你的任務應該處理異常,如果你的錯誤處理策略沒有包含某些東西(在任務中使用catch-all處理程序與在其他任何地方使用它一樣糟糕),仍然應該觀察它。

有很多方法可以使用得到的Task。哪個更合適取決於你的情況。如果您的外部循環本身是async,則結果爲await就足夠了(如果它是同步的,則使用Task.Wait())。這意味着直到任務完成後循環纔會繼續;如果您真的想要並行運行所有任務,請將其收集在Task[]中,並使用await Task.WhenAll()(對於同步代碼,請使用Task.WaitAll())。

如果您希望所有任務並行運行並且不關心任何結果,則最少可以使用Task.ContinueWith(),通過TaskContinuationOptions.OnlyOnFaulted運行日誌記錄代碼。這是在不丟棄實際錯誤的情況下「最大限度地消除和遺忘」。

一旦你修復了這個問題並且可以觀察異步代碼的任何錯誤,你就可以調試實際的問題了。