7

我正在用 C# 编写一个下载器,但遇到了以下问题:我应该使用哪种方法来并行下载并更新我的 GUI?

在我的第一次尝试中,我使用了 4 个线程,在每个线程完成后我又开始了另一个:主要问题是我的 cpu 在每个新线程启动时都会 100% 运行。

谷歌搜索,我发现 BackgroundWorker 和 ThreadPool 的存在:声明我想用我正在下载的每个链接的进度更新我的 GUI,最好的解决方案是什么?

1)创建4个不同的BackgroundWorker,为每个ProgressChanged事件附加一个委托给我的GUI中的一个函数以更新进度?

2) 使用 ThreadPool 并将最大和最小线程数设置为相同的值?

如果我选择#2,当队列中没有更多线程时,它会停止 4 个工作线程吗?它会暂停他们吗?由于我必须下载不同的链接列表(每个链接 20 个链接)并在一个完成后从一个链接移动到另一个,线程池是否在每个列表之间启动和停止线程?

如果我想更改实时工作线程的数量并决定使用 ThreadPool,从 10 个线程更改为 6 个,它会抛出异常并停止 4 个随机线程吗?

这是唯一让我头疼的部分。我提前感谢你们每一个人的回答。

4

4 回答 4

11

我建议使用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。

于 2011-08-02T15:04:19.400 回答
4

我认为您应该考虑使用Task Parallel Library,它是 .NET 4 中的新功能,旨在解决这些类型的问题

于 2011-08-02T14:16:33.057 回答
0

100% 的 cpu 负载与下载无关(因为您的网络实际上总是瓶颈)。我会说你必须检查你等待下载完成的逻辑。

您可以发布一些您多次启动的线程代码的代码吗?

于 2011-08-02T14:16:11.330 回答
0

通过创建 4 个不同的后台工作程序,您将创建不再干扰您的 GUI 的单独线程。后台工作人员很容易实现,据我了解,它们将完全按照您的需要执行。

就我个人而言,我会这样做,只是让其他人在前一个完成之前不要开始。(或者可能只有一个,并允许它以正确的顺序一次执行一个方法。)

仅供参考 - 后台工作人员

于 2011-08-02T14:24:06.320 回答