概念:我正在制作一个从给定URL下载文件的C#应用程序。文本框,添加的网址,文件下载,每个事件都以正确的方式发生。
我正在尝试重新创建此程序以逐个下载多个文件。我有一个带有一个url / line的文本框,解析正确,我将所有链接放在文本框中的字符串数组中。然后它开始下载异步,我想让它一个接一个地下载,所以我在foreach循环中进行了一个while循环,因为我不想在下一个URL完成下载之前转到下一个url。 / p>
问题是:我进入一个无限循环(虽然我之前做了这个工作(idk如何),如果我在while循环中放置一个消息框(注意:我在一分钟前重试,它没有做到这一点)这次))。
我只会展示代码段:
foreach (string url in urllist)
{
isdonwloaded = false;
string filename = url.Split('/').Last();
label3.Text = filename;
webclient.DownloadFileAsync(new Uri(url), @"C:\Users\Krisz" + @"\" + filename);
while (!isdonwloaded) // this was the first idea, but with webclient.IsBusy it did the same thing
{
// MessageBox.Show(counter);
Thread.Sleep(1000);
label8.Text = "Download in progress...";
}
counter++;
label8.Text = "Done!";
}
// Events:
webclient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webc_DownloadProgressChanged);
webclient.DownloadFileCompleted += new AsyncCompletedEventHandler(webc_DownloadFileCompleted);
// The DownloadFileCompleted event:
void webc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
label7.Text = String.Format("Files {0} / {1}", counter, arraylength(urllist));
isdonwloaded = true;
}
我已经调查了这个帖子:WebClient.DownloadFileAsync - Download files one at a time,但我无法以这种方式使其工作。 (也许我误解了什么?)
有人可以给我一些提示吗,我做错了什么?我从来没有真正使用过事件,因此遇到错误只是时间问题。 每一点帮助都有很大帮助,这个程序对我来说很有用。
答案 0 :(得分:9)
好的,首先,了解为什么您的代码现在不起作用。
想象一下你在办公室里有两个人。两者都有"在盒子里#34;他们的工作流程是:他们检查他们的收件箱。如果收件箱中有任务,则他们执行任务直到完成,然后再次检查他们的收件箱,重复。
工人1在收件箱中收到一条消息,说明您的下一个任务是:
工作人员1关闭开关并将以下任务放入工作人员2的收件箱中:
然后,工人1检查开关是否打开。不是,所以工人1去睡觉。
工作人员2下载该文件,然后在工作人员1的收件箱中发送消息,说明:
现在你明白为什么工人1会永远睡觉,对吧?该开关永远不会被翻转,因为翻转该开关是工作人员的工作,而工作人员1正在睡觉直到它被翻转。在当前任务完成之前,工作人员1不会查看其收件箱,并且当前任务没有完成,直到该开关被翻转为止。
这给了我们一个解决方案的想法,但它不是一个好的。
解决这个问题的廉价,肮脏,危险和不明智的方法是使用" DoEvents"而不是"睡眠"。这将任务更改为:
这解决了您的直接问题,但它引入了新问题。我们现在不再拥有干净的工作流程;一个收件箱任务可以生成第二个收件箱任务,这可以反过来产生第三个收件箱任务。任务可以变为“重入”,其中一个任务最终会启动第二个版本的本身。这种解决方案不够优雅,适用于难以调试的情况。理想情况下,您希望收件箱任务具有在旧的完成后启动新的的属性,而不是,而旧的仍然有工作要做。
对您的问题(如果您使用的是C#5)更好的廉价和肮脏的解决方法是使用
await Task.Delay(1000);
而不是Sleep
或DoEvents
。这对工作流程进行了微妙的改变。基本上它变成了:
如果告诉worker 3向worker 1发送一个新任务,那么它发送的新任务是:
您会看到如何巧妙但正确地更改工作流程?现在,worker 1将标签更改为下载,向worker 2发送消息,检查交换机,向worker 3发送消息,然后返回其收件箱。 Worker 2执行下载并向worker发送消息1. Worker 1翻转开关并返回收件箱。工作人员3向工作人员发送消息1.工作人员1检查交换机,将标签更改为完成,然后返回收件箱。
现在没有任务告诉您在收件箱中查找更多任务。每个收件箱任务按顺序处理:以后到达的任务始终在较早到达的任务完成后启动。
然而,最好的解决方案是拥有一个DownloadClientAsync
版本,它本身可以返回一个可以等待的任务。不幸的是,这是无效的。构建一个特殊版本的DownloadClientAsync,返回可以等待的Task
作为练习。一旦你有了这样一个帮助方法,那么代码变得微不足道;你只是await
那个任务。
答案 1 :(得分:2)
您是否尝试将事件订阅代码从当前行转移到以下行:
webclient.DownloadFileAsync(new Uri(url), @"C:\Users\Krisz" + @"\" + filename);
webclient.DownloadFileCompleted -= new AsyncCompletedEventHandler(webc_DownloadFileCompleted);
//Also it's a good practice to unsubscribe to event once after we are out-of-scope.
webclient.DownloadFileCompleted += new AsyncCompletedEventHandler(webc_DownloadFileCompleted);
当我这样做时,剩下的工作对我来说很好,否则你告诉屏幕是空白的。
答案 2 :(得分:1)
我认为这与BackgroundWorker类非常相似,其中CompletedEventHandler在主线程上运行。 (目前我找不到任何确认结果的结果。)
这意味着您的主循环永远不会被中断 - 您必须退出主URL循环并返回到UI,然后才能触发webc_DownloadFileCompleted。
一种可能的解决方法是在第一个URL上运行SINGLE webclient异步下载,然后返回主UI。您的webc_DownloadFileCompleted函数可以重新发出下一次异步下载调用。