问题描述
概念:我正在制作一个从给定的URL下载文件的C#应用程序。文本框,添加的URL,文件下载,每个事件都以正确的方式发生。我正在尝试重新创建此程序,逐个下载多个文件。我有一个文本框与一个url /行,解析正确发生,我有一个字符串数组中放置在文本框中的所有链接。然后它开始下载异步,我想让它逐个下载,所以我在foreach循环中做了一个循环,因为我不想去下一个url,直到当前的完成下载。 / p>
问题是:我进入无限循环(虽然我以前做过这个工作(idk如何),如果我在while循环中放置了一个消息框(注意:我重试了一分钟前,这次没有这样做))。
我只会显示代码段:
foreach(urllist中的字符串url)
{
isdonwloaded = false;
string filename = url.Split('/')。Last();
label3.Text = filename;
webclient.DownloadFileAsync(new Uri(url),@C:\Users\Krisz+ @\+ filename);
while(!isdonwloaded)//这是第一个想法,但是与webclient.IsBusy做了同样的事情
{
// MessageBox.Show(counter);
Thread.Sleep(1000);
label8.Text =正在下载...;
}
counter ++;
label8.Text =完成!;
}
//事件:
webclient.DownloadProgressChanged + = new DownloadProgressChangedEventHandler(webc_DownloadProgressChanged);
webclient.DownloadFileCompleted + = new AsyncCompletedEventHandler(webc_DownloadFileCompleted);
// DownloadFileCompleted事件:
void webc_DownloadFileCompleted(object sender,AsyncCompletedEventArgs e)
{
label7.Text = String.Format(Files {0} / {1},counter,arraylength(urllist));
isdonwloaded = true;
}
我已经研究了这个线程:,但我无法使其工作无论是这样。 (也许我误解了某些东西?)
有人可以给我一些提示,我做错了什么?我从来没有真正使用过事件,所以只是时间到了错误。
每一点帮助都是非常惊人的,这个程序对我来说是一个有用的一个。
关闭,了解为什么你的代码现在不工作。
想象一下,你在办公室里有两个人。两者都有在盒子里。他们的工作流程是:他们检查他们的收件箱。如果收件箱中有任务,则他们执行任务直到完成,然后再次检查其收件箱,重复。
工作者1在收件箱中收到一条消息,表示您的下一个任务是:
- 关闭开关
- 将标签更改为下载
- 告诉worker 2下载文件
- 返回上一步
- 将标签更改为完成
- 此任务现在完成
检查交换机是否打开,如果它已经脱离了循环;
工作人员1关闭电源,并将以下任务放在工作人员2的收件箱中:
- 下载文件
- 告诉worker 1打开开关
- 此任务已完成
工作人员1然后检查开关是否打开。没有,所以工作人员1去睡觉。
Worker 2下载文件,然后在worker 1的收件箱中发出一条消息:
- 打开开关
- 此任务完成
现在你明白为什么工人1永远睡觉,对吧?该切换永远不会被翻转,因为它是工作人员1的工作来翻转该开关,而工作人员1正在睡觉直到被翻转。在当前任务完成之前,工作人员1不会看到他们的收件箱,而当前任务没有完成,直到该开关被翻转。
这给了我们一个想法一个解决方案,但它不是一个好的。
解决这个问题的便宜,肮脏,危险和不好的方法是使用DoEvents而不是睡觉。将任务更改为:
- 关闭开关
- 将标签更改为下载
- 告诉工作人员2下载文件
- 检查交换机是否打开 - 如果它已经脱离了循环;如果没有,请检查您的收件箱中的消息,并做任何你在那里找到的东西。
- 返回上一步
- 将标签更改为done
- 此任务已完成
这可以解决您的直接问题,但会引入新的问题。我们现在不再有干净的工作流程;一个收件箱任务可以产生第二个收件箱任务,从而可以产生第三个收件箱任务。任务可以变成重入,其中一个任务最终会启动本身的第二个版本。这个解决方案是不合时宜的,因此难以调试的情况。理想情况下,您希望您的收件箱任务具有在旧的完成之后开始新的的属性,而旧的仍然有工作要执行。 p>
对于您的问题(如果您使用C#5),更好的便宜和肮脏的修复是使用
等待Task.Delay(1000);
而不是睡眠
或的DoEvents
。这对工作流程进行了微妙的改变。基本上它变成:
- 关闭开关
- 将标签更改为下载 li>
- 告诉worker 2下载文件
- 检查开关是否在
- 然后将标签更改为完成;这个任务完成了。
- 如果不是,请求工作人员在一秒钟内给我一个任务;此任务已完成。
如果worker 3被告知要向worker 1发送一个新任务,那么它发送的新任务是: / p>
- 检查开关是否开启
- 如果是,则将标签更改为完成;这个任务完成了。
- 如果不是,请求工作人员在一秒钟内给我一个任务;此任务已完成。
您是否看到了如何巧妙地正确更改工作流程?现在工作人员1将标签更改为下载,向工作人员2发送消息,检查交换机,向工作人员3发送消息,然后返回到收件箱。工作人员2下载并向工作人员1发送消息。工作人员1翻转开关并返回到收件箱。工作人员3向工作人员1发送消息。工人1检查交换机,将标签更改为完成,并返回到收件箱。
现在,没有任务可以让您在收件箱中查看更多的任务。每个收件箱任务按照顺序进行处理:迟到的任务总是在早期到达的任务完成后始终启动。
然而,最好的解决方案是拥有一个版本的 DownloadClientAsync
,它本身可以返回一个可以等待的任务。不幸的是,它是无效的返回。构建一个特殊版本的DownloadClientAsync,返回可以等待的任务
作为一个练习。一旦你有这样的帮助方法,那么代码变得微不足道您只需等待
该任务。
The conception: I'm making a C# app which downloads files from a given URL. Textbox, url added, file downloads, every event occurs in the correct way.
I'm trying to recreate this program to download multiple files, one by one. I have a textbox with one url/line, parsing happens correctly, I have all the links in a string array which was placed in the textbox. Then it starts downloading async, and I wanted to make it download only one by one, so I made a while loop in a foreach loop, as I don't want to go to the next url until the current one has finished downloading.
The problem is: I get in an infinite loop (though I made this work before (idk how), if I placed a messagebox in the while loop (note: I retried a minute ago, it didn't do the trick this time)).
I'll just show the code snippet:
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;
}
I have looked into this thread: WebClient.DownloadFileAsync - Download files one at a time , but I couldn't manage to make it work either that way. (Maybe I misunderstood something?)
Can someone give me some hints, what did I do wrong? I never really used events, so it was only matter of time to run into an error.Every bit of help is greatly appreaciated, this program would be a useful one for me.
OK, first off, understand why your code isn't working now.
Imagine you have two people in an office. Both have "in boxes". Their workflow is: they check their inbox. If there's a task in the inbox then they perform the task until it is finished, and then check their inbox again, repeat.
Worker 1 gets a message in their inbox that says your next task is:
- turn the switch off
- change the label to "downloading"
- tell worker 2 to download the file
- check to see if the switch is on -- if it is then break out of the loop; if not then go to sleep for one second.
- go back to the previous step
- change the label to "done"
- this task is now finished
Worker 1 turns the switch off and puts the following task in worker 2's inbox:
- download the file
- tell worker 1 to turn the switch on
- this task is finished
Worker 1 then checks to see if the switch is on. It is not, so worker 1 goes to sleep.
Worker 2 downloads the file, and then puts a message in worker 1's inbox that says:
- turn the switch on
- this task is finished
And now you see why worker 1 sleeps forever, right? That switch is never going to be flipped because it is worker 1's job to flip that switch, and worker 1 is sleeping until it is flipped. Worker 1 does not look in their inbox until the current task is finished, and the current task doesn't finish until that switch is flipped.
This gives us an idea for a solution, but it is not a good one.
The cheap, dirty, dangerous and ill-advised way to solve this problem is to use "DoEvents" instead of "Sleep". That changes the task to:
- turn the switch off
- change the label to "downloading"
- tell worker 2 to download the file
- check to see if the switch is on -- if it is then break out of the loop; if not then check your inbox for messages and do anything you find in there.
- go back to the previous step
- change the label to "done"
- this task is finished
This solves your immediate problem but it introduces new problems. We now no longer have a clean workflow; one inbox task can spawn a second inbox task, which can in turn spawn a third inbox task. Tasks can become "re-entrant", where one task ends up starting a second version of itself. This solution is inelegant and makes for situations that are difficult to debug. Ideally you want your inbox tasks to have the property that a new one is started after the old one is finished, not while the old one still has work to do.
A better cheap-and-dirty fix for your problem (if you are using C# 5) is to use
await Task.Delay(1000);
instead of Sleep
or DoEvents
. That makes a subtle change to the workflow. Basically it becomes:
- turn the switch off
- change the label to "downloading"
- tell worker 2 to download the file
- check to see if the switch is on
- if it is on then change the label to "done"; this task is finished.
- if not then ask worker 3 to send me a task in one second; this task is finished.
If worker 3 is told to send worker 1 a new task, then the new task it sends is:
- check to see if the switch is on
- if it is on then change the label to "done"; this task is finished.
- if not then ask worker 3 to send me a task in one second; this task is finished.
You see how that subtly but correctly changes the workflow? Now worker 1 changes the label to downloading, sends a message to worker 2, checks the switch, sends a message to worker 3, and then goes back to its inbox. Worker 2 does the download and sends a message to worker 1. Worker 1 flips the switch and goes back to the inbox. Worker 3 sends a message to worker 1. Worker 1 checks the switch, changes the label to done, and goes back to the inbox.
Now no task tells you to look in your inbox for more tasks. Each inbox task is processed in order: later-arriving tasks are always started after earlier-arriving tasks are finished.
The best solution however would be to have a version of DownloadClientAsync
that could itself return a task that could be awaited. Unfortunately, it is void-returning. Building a special version of DownloadClientAsync that returns a Task
that can be awaited is left as an exercise. Once you have such a helper method then the code becomes trivial; you just await
that task.
这篇关于使用WebClient下载多个文件时无限循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!