2015-03-02 159 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运行日志记录代码。这是在不丢弃实际错误的情况下“最大限度地消除和遗忘”。

一旦你修复了这个问题并且可以观察异步代码的任何错误,你就可以调试实际的问题了。