C# 下载器:我应该使用线程、BackgroundWorker 还是 ThreadPool?

发布于 2024-11-27 21:39:29 字数 582 浏览 5 评论 0 原文

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

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

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

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

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

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

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

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

I'm writing a downloader in C# and stopped at the following problem: what kind of method should I use to parallelize my downloads and update my GUI?

In my first attempt, I used 4 Threads and at the completion of each of them I started another one: main problem was that my cpu goes 100% at each new thread start.

Googling around, I found the existence of BackgroundWorker and ThreadPool: stating that I want to update my GUI with the progress of each link that I'm downloading, what is the best solution?

1) Creating 4 different BackgroundWorker, attaching to each ProgressChanged event a Delegate to a function in my GUI to update the progress?

2) Use ThreadPool and setting max and min number of threads to the same value?

If I choose #2, when there are no more threads in the queue, does it stop the 4 working threads? Does it suspend them? Since I have to download different lists of links (20 links each of them) and move from one to another when one is completed, does the ThreadPool start and stop threads between each list?

If I want to change the number of working threads on live and decide to use ThreadPool, changing from 10 threads to 6, does it throw and exception and stop 4 random threads?

This is the only part that is giving me an headache.
I thank each of you in advance for your answers.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

掩耳倾听 2024-12-04 21:39:29

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

I would suggest using WebClient.DownloadFileAsync for this. You can have multiple downloads going, each raising the DownloadProgressChanged event as it goes along, and DownloadFileCompleted when done.

You can control the concurrency by using a queue with a semaphore or, if you're using .NET 4.0, a BlockingCollection. For example:

// 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);
}

There's no need for explicitly managing threads or going to the TPL.

偏爱你一生 2024-12-04 21:39:29

我认为您应该考虑使用任务并行库,这是新的在 .NET 4 中,旨在解决这些类型的问题

I think you should look into using the Task Parallel Library, which is new in .NET 4 and is designed for solving these types of problems

空气里的味道 2024-12-04 21:39:29

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

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

Having 100% cpu load has nothing to do with the download (as your network is practically always the bottleneck). I would say you have to check your logic how you wait for the download to complete.

Can you post some code of the thread's code you start multiple times?

鸵鸟症 2024-12-04 21:39:29

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

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

仅供参考 - 后台工作者

By creating 4 different backgroundworkers you will be creating seperate threads that will no longer interfere with your GUI. Backgroundworkers are simple to implement and from what I understand will do exactly what you need them to do.

Personally I would do this and simply allow the others to not start until the previous one is finished. (Or maybe just one, and allow it to execute one method at a time in the correct order.)

FYI - Backgroundworker

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文