等待Task.WhenAll似乎完成,但它永远不会到达下一行代码

问题描述:

我有一个简单的文件下载器,我从服务器获取一些文件,然后显示一条消息,通知它是成功还是失败。下面的代码:等待Task.WhenAll似乎完成,但它永远不会到达下一行代码

public async Task GetFiles(IEnumerable<string> urlList) 
{ 
    //some variable declaring, setting etc. 

    await Task.WhenAll(urlList.Select(url => Task.Run(() => DownloadFile(url, dir, count++))).ToList()); 
    //problem lies here, the WhenAll never completes 
    if (fileDownloadError) 
    { 
     Directory.Delete(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "downloads")); 
     MessageBox.Show("Fail"); 
     return; 
    } 
    MessageBox.Show("Success"); 
} 

internal async Task DownloadFile(string url, string dir, int count) 
{ 
    string filename = HelperClass.GetFilenameFromUrl(url); 
    long a = 0; 
    newList.Add(a); 
    using (var client = new WebClient()) 
    { 
     TempDownload tp = new TempDownload() 
     { 
      id = count 
     }; 

     client.DownloadProgressChanged += new DownloadProgressChangedEventHandler((sender, e) => ProgressChanged(sender, e, tp)); 
     client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed); 

     await client.DownloadFileTaskAsync(url, string.Concat(dir, "\\", filename)); 
    } 
} 

ProgressBarDownloadProgressChanged事件改变达到100%,文件被正确下载,但代码中的WhenAll从未执行后,也就是它从来没有击中if (fileDownloadError)断点,没有消息框显示等。有什么我在这里失踪?

+0

放置代码,看看你得到一个异常 – jdweng

+0

也许有些线程异常在你的代码中。注释掉“DownloadProgressChaged”和“DownloadFileCompleted”事件,看看它是否有效。如果有效,问题出在这些事件处理程序上。 – AjS

+0

@jdweng我试过了,没有,它从来没有捕捉到任何东西,只是愉快地挂在WhenAll -AjS注释事件处理程序除了阻止相关的进度条工作之外没有任何作用 – Janushoff

由于死锁,断点从不碰撞if语句。将ConfigureAwait(false)添加到所有等待中。 EX-

await Task.WhenAll(urlList.Select(url => Task.Run(() => DownloadFile(url, dir, count++))).ToList()).ConfigureAwait(false); 

await client.DownloadFileTaskAsync(url, string.Concat(dir, "\\", filename)).ConfigureAwait(false); 

另一个optionto解决这个问题是,不使用等待或UI线程的结果。使用异步地等待着异常处理程序

EX-

private async void button1_Click(object sender, EventArgs e) 
{ 
      await GetFiles(); 
}  

对于细节,

https://msdn.microsoft.com/en-us/magazine/gg598924.aspx

https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html

+0

这确实会解除代码,谢谢。然而,我将不得不阅读一下你提供的内容来理解为什么它首先锁定了锁。 – Janushoff

+0

简而言之,死锁细节:UI线程正在等待任务完成。当任务完成并准备好返回到UI线程时,发生死锁。 UI线程等待任务完成并在UI线程上等待任务。通过使用ConfigureAwait(false),任务可以继续使用线程池中的任何线程(不会等待UI线程)。 – Kaushal

+1

另一个解决此问题的方法是不在UI线程上使用Wait或Result。在UI线程中使用异步等待 – Kaushal