我在C#中编写了一个下载程序并停止了以下问题:我应该使用哪种方法来并行化我的下载并更新我的GUI?
在我的第一次尝试中,我使用了4个线程,并且在每个线程完成时我开始了另一个:主要问题是我的cpu在每个新线程开始时都是100%。
谷歌搜索,我发现BackgroundWorker和ThreadPool的存在:说我想用我下载的每个链接的进度来更新我的GUI,最好的解决方案是什么?
1)创建4个不同的BackgroundWorker,附加到每个ProgressChanged事件,委托GUI中的函数来更新进度?
2)使用ThreadPool并将最大和最小线程数设置为相同的值?
如果我选择#2,当队列中没有更多线程时,是否会停止4个工作线程?它暂停了吗?因为我必须下载不同的链接列表(每个链接20个链接)并在完成一个链接时从一个链接移动到另一个链接,所以ThreadPool是否在每个列表之间启动和停止线程?
如果我想更改live的工作线程数并决定使用ThreadPool,从10个线程更改为6,是否会抛出异常并停止4个随机线程?
这是让我头疼的唯一部分。 我提前感谢你们每个人的答案。
答案 0 :(得分:10)
我建议使用WebClient.DownloadFileAsync
。您可以进行多次下载,每次下载都会引发DownloadProgressChanged
事件,完成后会DownloadFileCompleted
。
您可以通过使用带有信号量的队列来控制并发,或者,如果您使用的是.NET 4.0,则可以BlockingCollection
。例如:
// Information used in callbacks.
class DownloadArgs
{
public readonly string Url;
public readonly string Filename;
public readonly WebClient Client;
public DownloadArgs(string u, string f, WebClient c)
{
Url = u;
Filename = f;
Client = c;
}
}
const int MaxClients = 4;
// create a queue that allows the max items
BlockingCollection<WebClient> ClientQueue = new BlockingCollection<WebClient>(MaxClients);
// queue of urls to be downloaded (unbounded)
Queue<string> UrlQueue = new Queue<string>();
// create four WebClient instances and put them into the queue
for (int i = 0; i < MaxClients; ++i)
{
var cli = new WebClient();
cli.DownloadProgressChanged += DownloadProgressChanged;
cli.DownloadFileCompleted += DownloadFileCompleted;
ClientQueue.Add(cli);
}
// Fill the UrlQueue here
// Now go until the UrlQueue is empty
while (UrlQueue.Count > 0)
{
WebClient cli = ClientQueue.Take(); // blocks if there is no client available
string url = UrlQueue.Dequeue();
string fname = CreateOutputFilename(url); // or however you get the output file name
cli.DownloadFileAsync(new Uri(url), fname,
new DownloadArgs(url, fname, cli));
}
void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
DownloadArgs args = (DownloadArgs)e.UserState;
// Do status updates for this download
}
void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
DownloadArgs args = (DownloadArgs)e.UserState;
// do whatever UI updates
// now put this client back into the queue
ClientQueue.Add(args.Client);
}
不需要显式管理线程或转到TPL。
答案 1 :(得分:4)
我认为您应该考虑使用Task Parallel Library,这是.NET 4中的新功能,旨在解决这些类型的问题
答案 2 :(得分:0)
拥有100%的CPU负载与下载无关(因为您的网络几乎总是瓶颈)。我会说你必须检查你的逻辑如何等待下载完成。
你可以多次发布一些线程代码的代码吗?
答案 3 :(得分:0)
通过创建4个不同的后台工作人员,您将创建不再干扰GUI的单独线程。背景工作者很容易实现,根据我的理解,他们将完全按照你的需要去做。
我个人会这样做,只是让其他人在前一个完成之前不开始。 (或者可能只有一个,并允许它以正确的顺序一次执行一个方法。)