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

C#Downloader:我应该使用Threads、BackgroundWorker还是ThreadPool?,c#,multithreading,backgroundworker,threadpool,download-manager,C#,Multithreading,Backgroundworker,Threadpool,Download Manager,我正在用C#编写一个下载程序,遇到了以下问题:我应该使用什么样的方法来并行下载和更新GUI 在我的第一次尝试中,我使用了4个线程,在每个线程完成时,我启动了另一个线程:主要问题是,每次新线程启动时,我的cpu都会100%运行 通过谷歌搜索,我发现了BackgroundWorker和ThreadPool的存在:如果我想根据下载的每个链接的进度更新GUI,那么最好的解决方案是什么 1) 创建4个不同的BackgroundWorker,在每个ProgressChanged事件上附加一个委派到我的GUI

我正在用C#编写一个下载程序,遇到了以下问题:我应该使用什么样的方法来并行下载和更新GUI

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

通过谷歌搜索,我发现了BackgroundWorker和ThreadPool的存在:如果我想根据下载的每个链接的进度更新GUI,那么最好的解决方案是什么

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

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

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

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

这是唯一让我头疼的部分。
我提前感谢各位的回答。

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


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

我认为您应该研究使用,这是.NET 4中的新功能,旨在解决这些类型的问题。

通过创建4个不同的BackgroundWorker,您将创建不再干扰GUI的独立线程。Backgroundworkers很容易实现,据我所知,他们会做您需要他们做的事情

就我个人而言,我会这样做,并简单地允许其他人不开始,直到前一个完成。(或者只执行一个,并允许它以正确的顺序一次执行一个方法。)


我建议为此使用
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);
}
//回调中使用的信息。
类下载参数
{
公共只读字符串Url;
公共只读字符串文件名;
公共只读网络客户端;
公共下载参数(字符串u、字符串f、网络客户端c)
{
Url=u;
文件名=f;
客户=c;
}
}
const int MaxClients=4;
//创建一个允许最大项目数的队列
BlockingCollection客户端队列=新建BlockingCollection(MaxClient);
//要下载的URL队列(无限制)
队列UrlQueue=新队列();
//创建四个WebClient实例并将它们放入队列
对于(int i=0;i0)
{
WebClient cli=ClientQueue.Take();//如果没有可用的客户端,则会阻塞
字符串url=UrlQueue.Dequeue();
字符串fname=CreateOutputFilename(url);//或以何种方式获取输出文件名
cli.DownloadFileAsync(新Uri(url)、fname、,
新的下载参数(url、fname、cli);
}
void DownloadProgressChanged(对象发送方,DownloadProgressChangedEventArgs e)
{
DownloadArgs args=(DownloadArgs)e.UserState;
//是否对此下载进行状态更新
}
void DownloadFileCompleted(对象发送方,AsyncCompletedEventArgs e)
{
DownloadArgs args=(DownloadArgs)e.UserState;
//做任何用户界面更新
//现在将此客户机放回队列中
添加(args.Client);
}

不需要显式管理线程或转到TPL。

为什么不使用线程池中的线程?使用
BackgroundWorker
不会创建新进程,而是在线程池线程上执行。他将不会创建单独的进程,而是创建单独的线程。我是否可以按照相同的路线用另一种解决方案来修改这个问题?具有parallel.foreach(url,url=>{/*do action*/})的backgroundworker;在它里面它更容易阅读(就像foreach),并且允许逻辑在BGW运行时继续。MaxDegreeOfParallelism似乎允许我设置线程/任务的最大数量,但不允许设置最小数量。不仅如此,我似乎无法在live上更改此值。不过建议不错,我认为line ClientQueue.Add(newwebclient());错误,应为ClientQueue.Add(cli)。无论如何,我认为这种方法有两个问题:1)我必须在下载之前指定文件名,但我事先不知道它的名称。我通常从http头响应的“内容处置”链接中获取名称。2) 我第一次编写应用程序时,在可用的选项中,有WebClient,但如果我没记错的话,它会在后台为每个链接打开InternetExplorer!我仍然记得弹出式的来自任何地方…我修复了你识别的错误。code>WebClient未打开IE。您可能正在考虑使用
WebBrowser
控件
WebClient
HttpWebRequest
HttpWebResponse
的包装器。如果需要标题中的信息,可以从
ResponseHeaders
属性中获取。以上只是一个例子。你的要求是