.net之在 C# 中一次下载多个 HTML

zhwl 阅读:16 2024-11-24 20:56:43 评论:0

我有一个需要下载的网址列表(> 10k),如果我使用单线程应用程序下载非常耗时,那么多线程或多个 backgroundworker 实例中哪个是更好的选择,为什么?

请您参考如下方法:

您应该使用的方法在很大程度上取决于您希望以多快的速度下载这 10,000 个页面以及您希望多久下载一次。

一般来说,单线程下载应用程序平均每秒下载一页。您的结果会因您下载的网站而异。从 yahoo.com 获取内容比从某人托管在电缆调制解调器上的服务器下载内容要快。单线程下载应用程序的好处在于它非常易于编写。如果您只需要下载这些页面一次,请编写单线程应用程序,让它工作,然后吃一顿长午餐。您将在大约三个小时内获得数据。

如果您有一台四核机器,您每秒可以处理大约四页。只需编写您的单线程应用程序,将您的 URL 列表分成四个相等的部分,启动您的应用程序的四个实例,然后定期吃午饭。当您回来时,您将获得数据。

如果您要定期下载这些页面,那么您可以编写程序来维护 BlockingCollection为您的网址。启动四个线程,每个线程主要执行以下操作:

while (queue not empty) 
{ 
    dequeue url 
    download page 
} 

这将在与单线程下载程序的四个独立实例相同的时间内执行。实际上,它的执行速度可能会稍微快一些,因为您没有拆分队列。因此,您不会遇到一个线程完成其队列并在仍有 URL 要下载时停止的问题。同样,该程序非常容易编写,您将在一个小时内完成这 10,000 页。

你可以比这快得多。在典型的电缆调制解调器速度下,您可以毫不费力地达到每秒近 20 页的速度。忘记使用 TPL 或 ThreadPool.QueueUserWorkItem 等。而是使用 WebClientDownloadDataAsync .创建一个队列,比如 10 个 WebClient 实例。然后,您的主线程执行此操作:

while (url queue is not empty) 
{ 
    client = dequeue WebClient // this will block if all clients are currently busy 
    url = dequeue url 
    client.DownloadDataAsync(url) 
} 

WebClient 实例的 DownloadDataCompleted下载完成时将调用事件处理程序,因此您可以保存数据。它还将 WebClient 实例放回队列中,以便可以重新使用。

同样,这是一种相当简单的方法,但它非常有效。它利用了 HttpWebRequest 的异步功能(这是 WebClient 用来做它的事情)。使用这种方法,您不会一直执行 10 个或更多线程。相反,线程池启动并仅使用所需数量的线程来读取数据和执行回调。如果您使用 TPL 或其他一些显式多线程技术,您最终会得到一堆线程,这些线程大部分时间在等待连接等时无所事事。

您必须考虑并发下载的数量(即队列中 WebClient 实例的数量)。您可以支持多少主要取决于您的 Internet 连接速度。它还将取决于 DNS 请求的平均延迟(可能会非常长)以及您从多少个不同的域下载。

使用多线程方法时的另一个注意事项是礼貌。如果所有 10,000 个 URL 都来自同一个域,您不希望同时发出 10 个请求。他们可能会认为您正试图进行 DOS 攻击并阻止您。如果这些 URL 仅来自少数几个域,则您需要限制连接。如果您只有少数来自任何一个特定域的 URL,那么这不是问题。


标签:C#
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号