这是我的全部源代码。我想下载x个文件(假设我们现在有2个文件要下载)。
我的主要观点是等待所有下载完成,然后继续。 我尝试了几点。
Task.WhenAll(List)->无法以我的实现方式工作
await->不可能在MainWindow函数中没有异步。
工作流程如下。 开始下载->已下载所有文件->显示Debug.Print
Atm:源代码输出为-> 所有下载完成 “下载完成” “下载完成”
应为: “下载完成” “下载完成” 所有下载完成
工作了几个小时,并没有进一步的步骤来解决问题。这是一个WPF应用程序。
private volatile bool _download_completed;
public bool DownloadCompleted { get { return _download_completed; } }
public MainWindow()
{
InitializeComponent();
ProcessService.FillProcessData();
ProcessService.CloseAllProcesses();
DownloadHelper.AddDownloadFiles();
foreach(KeyValuePair<string,Uri> file in DownloadHelper.DownloadFiles)
{
DownloadFile(file.Key, file.Value);
}
Debug.Print("All Downloads done.");
}
private void DownloadFile(string Filename, Uri Uri)
{
Console.WriteLine(Filename + " " + Uri);
_download_completed = false;
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
client.DownloadFileTaskAsync(Uri, Filename);
}
private void DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e)
{
this.Dispatcher.Invoke(() => {
progressBar.Value = e.ProgressPercentage;
StatusLabel.Content = e.ProgressPercentage + " % complete... ( " + e.BytesReceived + " / " + e.TotalBytesToReceive + ")";
});
}
private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("Download completed");
this.Dispatcher.Invoke(() =>
{
StatusLabel.Content = "Download Finished";
});
_download_completed = true;
}
}
答案 0 :(得分:0)
您需要使用任务来实现这一目标
首先使您的下载功能异步
private async TaskDownloadFile(string Filename, Uri Uri)
{
Console.WriteLine(Filename + " " + Uri);
_download_completed = false;
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
await client.DownloadFileTaskAsync(Uri, Filename);
}
使方法异步,然后等待DownloadFileTaskAsync方法。
如果您要进行多次下载并没有太大意义,我建议删除DownloadFileCompeted和DownloadProgressChanged事件
现在,您已使方法async进行以下操作
var file1DownloadTask = TaskDownloadFile("filename","url");
var file2DownloadTask = TaskDownloadFile("filename","url");
这时您有两个任务
您可以使用Task.WaitAll(list_of_taks_goes_here)
Task.WaitAll(file1DownloadTask,file2DownloadTask )
wait all方法将等待所有任务完成,因此在这两个任务完成后调用下一行代码
然后,您可以在Task.WaitAll行之后更改ui或您需要执行的任何操作
您还可以执行以下操作
await TaskDownloadFile("filename","url");
await TaskDownloadFile("filename2","url");
将等待下载完成,然后再继续下一行,但是通过这种方式,您必须在编写代码时了解所有文件,使用第一种方法,您可以拥有一个文件列表,从中可以制作任务并将其传递给Task.WaitAll
答案 1 :(得分:0)
请注意,以下内容会将完整的下载内容下载到内存中,如果下载的文件很大,请考虑重写以在HttpClient上调用GetStreamAsync并流式传输到文件,但这将在合理的大小下完成。
您通常希望重用HttpClient,但在完成后进行处置,因此,如果这是WPF,则可以构造并在启动时进行保留并最终处置,但这可能也是合理的。
编辑:请注意,如果锁定UI线程成为问题,您可能需要在另一个线程上执行此操作并分派完成。
void Main()
{
using (var client = new HttpClient())
{
var tasks = new List<Task>();
var files = new Dictionary<string, string>();
files.Add("c:\\temp\\MyFilename1.md", "https://raw.githubusercontent.com/aspnet/AspNetCore/master/README.md");
files.Add("c:\\temp\\MyFilename2.md", "https://raw.githubusercontent.com/aspnet/AspNetCore/master/README.md");
foreach (var file in files)
{
tasks.Add(DownloadFileAsync(client, file.Key, file.Value));
}
Task.WaitAll(tasks.ToArray());
}
}
async Task DownloadFileAsync(HttpClient client, string filename, string url)
{
var contents = await client.GetStringAsync(url);
File.WriteAllText(filename, contents);
}
答案 2 :(得分:0)
我的主要观点是等待所有下载完成然后继续。
我鼓励您退后一步,考虑应用程序的UX设计。永远不要等待I / O并阻塞UI线程,这是一个好主意。
尤其是,您的ViewModel构造函数绝不能在I / O上 block 。 ViewModel构造函数应立即并同步完成,以便可以立即并同步显示您的UI。
现在,构造函数可以开始异步操作;没关系。它只是不应该等待它们完成。您的构造函数应将ViewModel初始化为显示“正在下载...”或类似内容的状态,并在下载完成(或随着下载进度)时,ViewModel可以更新其状态以反映该状态。 / p>
有关此模式的更多信息,请参阅我在data binding to asynchronous operations上的MSDN文章。