问题
I am trying to download a file, wait for the file to finish downloading, and then read the file afterwards. I have the following methods to do this:
private async Task startDownload(string link, string savePath)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
await client.DownloadFileTaskAsync(new Uri(link), savePath);
}
private void checkUpdateButton_Click(object sender, EventArgs e)
{
Task task = Task.Factory.StartNew(() => startDownload(versionLink, versionSaveTo));
task.Wait();
if (task.IsCompleted)
{
checkVersion();
}
}
The checkVersion() method reads the file that was downloaded. This is throwing an IOException saying that the file is in use by something else and cannot be read. I thought that having task.Wait would prevent the rest of the method from executing until the task was finished?
回答1:
Task.Wait will block the current thread (in this case, the UI thread) and wait until the task is completed. In this case, the task is completing with an error, so Task.Wait will throw that error wrapped in an AggregateException.
As others noted, you should be using await instead of Wait. Also, DownloadFileCompleted doesn't make sense since you're using DownloadFileTaskAsync (and not DownloadFileAsync); and StartNew is unnecessary since the download is asynchronous.
Oh, and let's dispose the WebClient and ensure our naming convention follows the Task-based Asynchronous Pattern.
private async Task startDownloadAsync(string link, string savePath)
{
using (var client = new WebClient())
{
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
await client.DownloadFileTaskAsync(new Uri(link), savePath);
}
}
private async void checkUpdateButton_Click(object sender, EventArgs e)
{
await startDownloadAsync(versionLink, versionSaveTo);
checkVersion();
}
回答2:
The function startDownload is already async, so it's going to start the Task and return right away. You can use ContinueWith to ensure the task is complete before calling checkVersion().
private void checkUpdateButton_Click(object sender, EventArgs e)
{
var task = startDownload(versionLink, versionSaveTo);
task.ContinueWith((x) => checkVersion());
}
An alternative, as Servy points out, would be to use async/await in the Click event.
private async void checkUpdateButton_Click(object sender, EventArgs e)
{
await startDownload(versionLink, versionSaveTo);
checkVersion();
}
回答3:
You'll need to await your Task.Factory.StartNew(...) call, that way it doesn't block the UI thread.
private async void button1_Click(object sender, EventArgs e)
{
Task task = await Task.Factory.StartNew(() => startDownload("http://www.zwaldtransport.com/images/placeholders/placeholder1.jpg", "" + "sd.jpg"));
}
private async Task startDownload(string link, string savePath)
{
WebClient client = new WebClient();
client.DownloadProgressChanged += Client_DownloadProgressChanged;
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
await client.DownloadFileTaskAsync(new Uri(link), savePath);
}
private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
checkVersion();
Console.WriteLine("Done, unless error or cancelled.");
}
private void Client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
Console.WriteLine("Progress changed.");
}
Image placeholder courtesy Google Images and some other website.
来源:https://stackoverflow.com/questions/25451392/task-wait-not-working-as-i-imagined