简体   繁体   English

C#Downloader:我应该使用Threads,BackgroundWorker还是ThreadPool?

[英]C# Downloader: should I use Threads, BackgroundWorker or ThreadPool?

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? 我正在用C#编写一个下载程序并停止了以下问题:我应该使用什么样的方法来并行化我的下载并更新我的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. 在我的第一次尝试中,我使用了4个线程,并且在每个线程完成时我开始了另一个:主要问题是我的cpu在每个新线程启动时都是100%。

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

1) Creating 4 different BackgroundWorker, attaching to each ProgressChanged event a Delegate to a function in my GUI to update the progress? 1)创建4个不同的BackgroundWorker,附加到每个ProgressChanged事件,委托我的GUI中的函数来更新进度?

2) Use ThreadPool and setting max and min number of threads to the same value? 2)使用ThreadPool并将最大和最小线程数设置为相同的值?

If I choose #2, when there are no more threads in the queue, does it stop the 4 working threads? 如果我选择#2,当队列中没有更多线程时,是否会停止4个工作线程? 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? 由于我必须下载不同的链接列表(每个链接20个链接)并在完成一个链接时从一个链接移动到另一个链接,ThreadPool是否在每个列表之间启动和停止线程?

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? 如果我想在live上更改工作线程的数量并决定使用ThreadPool,从10个线程更改为6,它是否会抛出异常并停止4个随机线程?

This is the only part that is giving me an headache. 这是让我头疼的唯一部分。 I thank each of you in advance for your answers. 我提前感谢你们每个人的答案。

I would suggest using WebClient.DownloadFileAsync for this. 我建议使用WebClient.DownloadFileAsync You can have multiple downloads going, each raising the DownloadProgressChanged event as it goes along, and DownloadFileCompleted when done. 你可以有多个下载去,每提高DownloadProgressChanged事件,因为它去一起,并且DownloadFileCompleted完成时。

You can control the concurrency by using a queue with a semaphore or, if you're using .NET 4.0, a BlockingCollection . 您可以通过使用带有信号量的队列来控制并发,或者,如果您使用的是.NET 4.0,则可以使用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. 不需要显式管理线程或转到TPL。

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

Having 100% cpu load has nothing to do with the download (as your network is practically always the bottleneck). 拥有100%的CPU负载与下载无关(因为您的网络几乎总是瓶颈)。 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? 你可以多次发布一些线程代码的代码吗?

By creating 4 different backgroundworkers you will be creating seperate threads that will no longer interfere with your GUI. 通过创建4个不同的后台工作人员,您将创建不再干扰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 仅供参考 - Backgroundworker

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM