我有一个程序,每5分钟获取~500个网页的HTML代码
它正常运行直到第一次失败(无法在6秒内下载源)
之后所有线程都将失败
如果我重新启动程序,它会再次正常运行,直到......
我错了,我应该做些什么来做得更好?
此功能每5分钟运行一次:
foreach (Company company in companies)
{
string link = company.GetLink();
Thread t = new Thread(() => F(company, link));
t.Start();
if (!t.Join(TimeSpan.FromSeconds(6)))
{
Debug.WriteLine( company.Name + " Fails");
t.Abort();
}
}
此函数下载HTML代码
private void F(Company company, string link)
{
try
{
string htmlCode = GetInformationFromWeb.GetHtmlRequest(link);
company.HtmlCode = htmlCode;
}
catch (Exception ex)
{
}
}
和这堂课:
public class GetInformationFromWeb
{
public static string GetHtmlRequest(string url)
{
using (MyWebClient client = new MyWebClient())
{
client.Encoding = Encoding.UTF8;
string htmlCode = client.DownloadString(url);
return htmlCode;
}
}
}
和Web客户端类
public class MyWebClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
HttpWebRequest request = base.GetWebRequest(address) as HttpWebRequest;
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
return request;
}
}
答案 0 :(得分:1)
如果您的foreach正在循环超过500家公司,并且每个公司都在创建一个新线程,那么您的互联网速度可能会成为瓶颈,您将在6秒内收到超时,并且经常会失败。
我建议你试试并行性。注意MaxDegreeOfParallelism
,它设置最大并行执行量。您可以调整它以满足您的需求。
Parallel.ForEach(companies, new ParallelOptions { MaxDegreeOfParallelism = 10 }, (company) =>
{
try
{
string htmlCode = GetInformationFromWeb.GetHtmlRequest(company.link);
company.HtmlCode = htmlCode;
}
catch(Exception ex)
{
//ignore or process exception
}
});
答案 1 :(得分:1)
我有四个基本建议:
HttpClient
代替过时的WebClient
。 HttpClient
可以本地处理异步操作,并且具有更大的灵活性可以利用。您甚至可以将下载的内容读取到不同线程上的字符串/流,因为您可以配置await
不安排回操作。或者甚至将HttpClientHandler
编程为在6秒后中断,如果超出则提高TaskCanceledException
。F
函数中所做的那样),因为它会破坏调试并混淆问题的真正原因。在正常操作期间,正确编写的程序永远不会引发异常。Task
进行多任务处理(例如,将其称为Task.Run(async delegate() { await yourTask(); })
(或AsyncContext.Run(...)
,如果您需要UI访问权限)并且它不会获胜阻止任何事情。GetInformationFromWeb
类目前毫无意义 - 而且您也毫无意义地生成多个客户端对象,因为一个HTTP客户端对象可以处理多个请求(如果您使用HttpClient
甚至没有额外的膨胀 - 您只需将其实例化为具有所有必要配置的静态全局变量,然后使用与client.GetStringAsync(Uri uri)
一样少的代码从任何地方调用它。