我需要使用WebClient
和线程创建多个文件下载程序,出于学习目的,我创建了此代码(如下所示),文件正确下载但有时下载停止并且不再开始运行。我也想控制ProgressBar
。这个代码的调用者是一个按钮。
for (int i = 0; i < 3; i++)
{
Start(
delegate
{
WebClient wc = new WebClient();
if(wc.IsBusy != true)
{
wc.DownloadFileAsync(new Uri("http://ipv4.download.thinkbroadband.com/10MB.zip"), @"D:\File" + Convert.ToString(i) + ".txt");
wc.DownloadProgressChanged +=
delegate(object sender1, DownloadProgressChangedEventArgs e1) {
foreach (ToolStripProgressBar it in statusStrip1.Items)
{
if ((it.Name == "pg" + Convert.ToString(x)) == true)
{
it.Control.Invoke((MethodInvoker)delegate() { it.Value = e1.ProgressPercentage; });
}
}
};
}
});
}
public static Thread Start(Action action)
{
Thread thread = new Thread(() => { action(); });
thread.Start();
return thread;
}
答案 0 :(得分:2)
由于以下几个原因,您的计划不起作用: (我相信)您正在使用多个进度条(每个线程一个),但WebClient不提供任何导致 DownloadProgressChanged 事件被触发的文件的信息,因此您需要一个WebClient实例文件。
第二,当您将循环变量 i 传递给wc.DownloadFileAsync(new Uri("http://ipv4.download.thinkbroadband.com/10MB.zip"), @"D:\File" + Convert.ToString(i) + ".txt");
方法时,您将捕获的引用传递给循环变量而不是值(因为您使用的是委托引用变量超出其范围,google为“Closure”),这意味着当文件开始下载时,它可能被复制到错误的文件中。您可以通过将值传递给委托来避免这种情况。
一个例子,它与你的程序不完全相同,但你可以很容易地修改它:
var progressBars = new ProgressBar[]
{
this.progressBar1,
this.progressBar2,
this.progressBar3
};
for (int i = 0; i < 3; i++)
{
var webClient = new WebClient();
webClient.DownloadFileAsync(new Uri("http://ipv4.download.thinkbroadband.com/10MB.zip"), @"E:\File" + Convert.ToString(i) + ".txt");
var progressBar = progressBars[i];
webClient.DownloadProgressChanged += (sender1, e1) =>
{
progressBar.Invoke((MethodInvoker)(() =>
{
progressBar.Value = e1.ProgressPercentage;
}));
};
}
答案 1 :(得分:0)
您没有保留对WebClient
的引用,因此垃圾收集器在完成启动请求后几乎可以在任何时候回收它。你需要在某处保留对它的引用,以便GC不能这样做。
幸运的是,这是一个相当可解决的问题。我们可以创建一个新类,它将为我们创建一个客户端,同时也将其存储在内部,直到它被处理掉:
public class ClientCreator
{
private static HashSet<WebClient> clients = new HashSet<WebClient>();
public static WebClient CreateClient()
{
WebClient client = new WebClient();
lock (clients)
clients.Add(client);
client.Disposed += (s, args) =>
{
lock (clients) clients.Remove(client);
};
return client;
}
}
现在你需要做的就是确保在你完成它之后处理你的网络客户端(无论如何你应该做的事情)。
添加其他处理程序时,只需添加wc.DownloadFileCompleted += (s, args) => wc.Dispose();
,以便在文件下载完成后将其丢弃。
还值得注意的是,根本不需要在这里创建额外的线程。您的方法本质上是异步的;几乎没有(CPU)时间运行。您可以删除所有代码以将其推送到另一个线程而不会丢失任何内容,而不会阻止您的UI。