我正在用 C# 编写一个下载程序,但遇到了以下问题:我应该使用哪种方法来并行下载和更新我的 GUI?
在我的第一次尝试中,我使用了 4 个线程,并且在每个线程完成后我启动了另一个线程:主要问题是我的 cpu 在每个新线程启动时都达到 100%。
谷歌搜索,我发现 BackgroundWorker 和 ThreadPool 的存在:声明我想用我正在下载的每个链接的进度更新我的 GUI,什么是最好的解决方案?
1) 创建 4 个不同的 BackgroundWorker,为每个 ProgressChanged 事件附加一个 Delegate 到我的 GUI 中的函数以更新进度?
2) 使用 ThreadPool 并将最大和最小线程数设置为相同的值?
如果我选择#2,当队列中没有更多线程时,是否会停止 4 个工作线程?它会暂停他们吗?由于我必须下载不同的链接列表(每个链接 20 个链接)并在一个完成后从一个链接移动到另一个链接,ThreadPool 是否在每个列表之间启动和停止线程?
如果我想更改实时工作线程的数量并决定使用线程池,从 10 个线程更改为 6 个,它会抛出异常并停止 4 个随机线程吗?
这是唯一让我头疼的部分。 我提前感谢你们每个人的回答。
最佳答案
我建议为此使用 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。
关于C# 下载器 : should I use Threads, BackgroundWorker 还是 ThreadPool?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6913487/