我有WPF应用程序处理很多网址(数千个),每个网址发送给它自己的线程,进行一些处理并将结果存储在数据库中。
网址可以是任何东西,但有些似乎是大页面,这似乎会大大提高内存使用率并使性能非常糟糕。我在网络请求上设置了超时,因此如果花费的时间超过20秒,则不会对该网址造成影响,但似乎没有太大区别。
以下是代码部分:
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(urlAddress.Address);
req.Timeout = 20000;
req.ReadWriteTimeout = 20000;
req.Method = "GET";
req.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
using (StreamReader reader = new StreamReader(req.GetResponse().GetResponseStream()))
{
pageSource = reader.ReadToEnd();
req = null;
}
它似乎也在读取器上停止/增加内存.ReadToEnd();
我原以为20秒的切断会有所帮助,有更好的方法吗?我假设使用asynch web方法并没有太大的优势,因为每个url下载都是自己的线程..
由于
答案 0 :(得分:2)
通常,it's recommended that you use asynchronous HttpWebRequests而不是创建自己的线程。我上面链接的文章还包括一些基准测试结果。
在您阅读要结束的流后,我不知道您对页面源的操作,using string can be an issue:
System.String类型用于任何.NET应用程序。我们有字符串 as:姓名,地址,描述,错误消息,警告甚至 应用程序设置。每个应用程序都必须创建,比较或 格式化字符串数据考虑到不变性和任何事实 对象可以转换为字符串,所有可用的内存都可以 吞噬了大量不需要的字符串重复或无人认领 字符串对象。
其他一些建议:
Stream
初始化解析器,而不是传入包含页面源的字符串(如果这是一个选项) 此外,您能告诉我们您获取页面的初始速率是什么以及它归结为什么?您在获取页面时是否看到Web请求中的任何错误/异常?
在评论部分,我注意到你创建了数千个线程,我会说你不需要这样做。从少量线程开始并不断增加它们直到您查看系统性能。一旦你开始添加线程并且性能看起来逐渐减少,那么就可以添加线程。我无法想象你需要超过128个线程(即使看起来很高)。创建固定数量的线程,例如64,让每个线程从队列中获取一个URL,获取页面,处理它,然后再返回从队列中获取页面。
答案 1 :(得分:1)
您可以使用缓冲区枚举而不是调用ReadToEnd,如果它花费的时间太长,那么您可以记录并放弃 - 例如:
static void Main(string[] args)
{
Uri largeUri = new Uri("http://www.rfkbau.de/index.php?option=com_easybook&Itemid=22&startpage=7096");
DateTime start = DateTime.Now;
int timeoutSeconds = 10;
foreach (var s in ReadLargePage(largeUri))
{
if ((DateTime.Now - start).TotalSeconds > timeoutSeconds)
{
Console.WriteLine("Stopping - this is taking too long.");
break;
}
}
}
static IEnumerable<string> ReadLargePage(Uri uri)
{
int bufferSize = 8192;
int readCount;
Char[] readBuffer = new Char[bufferSize];
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
using (StreamReader stream = new StreamReader(response.GetResponseStream(), Encoding.UTF8))
{
readCount = stream.Read(readBuffer, 0, bufferSize);
while (readCount > 0)
{
yield return new string(readBuffer, 0, bufferSize);
readCount = stream.Read(readBuffer, 0, bufferSize);
}
}
}
答案 2 :(得分:1)
Lirik有很好的总结。
我想补充一点,如果我实现这个,我会创建一个单独的进程来读取页面。所以,这将是一个管道。第一阶段将下载URL并将其写入磁盘位置。然后将该文件排队到下一个阶段。下一阶段从磁盘读取并进行解析&amp;数据库更新。这样,您将获得下载和解析的最大吞吐量。你也可以调整你的线程池,以便你有更多的工作人员解析,等等。这个架构也非常适合分布式处理,你可以有一台机器下载,另一台主机解析/等。
另一件需要注意的事情是,如果您从多个线程击中同一台服务器(即使您使用的是Async),那么您将会遇到最大传出连接限制。您可以限制自己保持低于该值,或者增加ServicePointManager类的连接限制。