C#多线程大规模解析

3
我正在制作一个“网络解析器”,但它只是针对一个网站,该网站将同时解析许多不同的页面。
目前,我需要解析大约300,000页,在相对较快的情况下(我只获取了一小部分信息,每个页面在我的网络上最多需要约3秒)。当然,900,000秒等于10天,这是可怕的性能。我希望将其减少到最多几个小时,我对时间和请求数量合理,但它仍然需要“快速”。我也知道我不能一次性处理300,000个请求,否则网站会阻止所有请求,因此每个请求之间将必须有几秒钟的延迟。
我目前在单个foreach循环中进行处理,没有利用任何多线程,但我知道我可以利用它,我不确定应该采取哪种线程池或其他类型的线程系统或设计。
基本上,我正在寻找有人指导我使用多线程的效率方向,以便我可以减轻在我的端口解析那么多页面所需的时间,提供某种线程系统或结构。
谢谢

http://msdn.microsoft.com/en-us/library/dd537609.aspx - Chris Pfohl
你是否已经掌握了所有30万个链接,还是在进行过程中不断地发现呢? - Jon Skeet
3
请查看更高级别的.NET多线程概念,例如async和Task Parallel Library。 - Robert Harvey
@jon - 我知道所有的300,000个链接从哪里开始,所以它不是一个真正的爬虫,只是一个解析器。 - sl133
听起来像是Parallel.ForEach是你的好帮手。你应该找出你的网站能够处理多少流量。 - Jon Skeet
显示剩余3条评论
2个回答

7
请查看这个问题的答案,因为听起来你可能想要查看Parallel.ForEach

有各种其他方法可以以多线程方式实现您想要做的事情。 为了让自己了解如何工作:

  1. 下载LINQPad。(应该是任何C#开发人员的先决条件,我的看法!)
  2. 在“示例”中,“下载/导入更多示例...”,并确保已下载“C#中的异步函数”。
  3. 通过样例进行学习,看它们如何配合使用。

实际上,这里是一个使用Uri的异步示例之一:

// The await keyword is really useful when you want to run something in a loop. For instance:

string[] uris =
{
    "http://linqpad.net",
    "http://linqpad.net/downloadglyph.png",
    "http://linqpad.net/linqpadscreen.png",
    "http://linqpad.net/linqpadmed.png",
};

// Try doing the following without the await keyword!

int totalLength = 0;
foreach (string uri in uris)
{
    string html = await (new WebClient().DownloadStringTaskAsync (new Uri (uri)));
    totalLength += html.Length;
}
totalLength.Dump();

// The continuation is not just 'totalLength += html.Length', but the rest of the loop! (And that final
// call to 'totalLength.Dump()' at the end.)

// Logically, execution EXITS THE METHOD and RETURNS TO THE CALLER upon reaching the await statement. Rather
// like a 'yield return' (in fact, the compiler uses the same state-machine engine to rewrite asynchronous
// functions as it does iterators).
//
// When the task completes, the continuation kicks off and execution jumps back into the middle of the 
// loop - right where it left off!

2
正如其他人提到的,使用并行处理可以让你开始。在 .Net 4.0 或更高版本中,它已经内置了,并且非常容易使用。如果您有一个 foreach 循环并想要添加并行处理,只需添加对 system.threading 的引用并添加 Parallel.ForEach。请记住,生成的线程数量没有限制,但是存在实际限制。内置的 .Net 库会为您自动管理线程池,并保护您免受细节方面的困扰。
如果您决定更改参数,请记住以下内容:
每个线程将使用约 1MB 的堆栈空间,这意味着对于每一千个线程,您将使用大约 1GB 的资源。但是,实际上,如果您创建 1000 个线程,处理速度很可能比仅使用一个线程更慢。通常,每个核心/处理器一个线程。因此,在四核/四处理器的情况下,一个很好的线程计数起点是 16,您可以根据程序的确切操作来调整它的值,以覆盖库正在执行的方式或自己编写代码。但我不建议这样做,因为库会自动处理这些内部细节。
我还要提到,单台机器可能无法满足您的需求,因此您的应用程序应该水平扩展,并能够添加新的机器并解析更多页面,而不仅仅是单独一台机器(无论是多线程还是其他方式)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接