关于c#:首次失败后,获取HTML响应失败

Getting HTML response fails respectively after first fail

我有一个程序,每5分钟可以获取大约500个网页的HTML代码

它运行正常,直到第一次失败(6秒内无法下载源)

之后,所有线程都将失败

如果我重新启动程序,它会再次正确运行,直到…

如果我错了,我该怎么做才能做得更好?

此功能每5分钟运行一次:

1
2
3
4
5
6
7
8
9
10
11
12
        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代码

1
2
3
4
5
6
7
8
9
10
11
private void F(Company company, string link)
    {
        try
        {
            string htmlCode = GetInformationFromWeb.GetHtmlRequest(link);
            company.HtmlCode = htmlCode;
        }
        catch (Exception ex)
        {
        }
    }

这门课:

1
2
3
4
5
6
7
8
9
10
11
12
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客户端类

1
2
3
4
5
6
7
8
9
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;
    }
}


我有四个基本建议:

  • 使用HttpClient而不是过时的WebClientHttpClient可以本机处理异步操作,并具有更大的灵活性。您甚至可以将下载的内容读取到不同线程上的字符串/流中,因为您可以配置await,而不是安排您的操作。或者甚至设定HttpClientHandler在6秒后中断,如果超过6秒,则升高TaskCanceledException
  • 避免吞咽异常(就像在F函数中那样),因为它会中断调试并混淆问题的真正原因。正确编写的程序在正常运行期间不会引发异常。
  • 您正在以一种无用的方式使用线程,在这种方式中,它们甚至不会重叠;它们只是在等待彼此启动,因为您在每个线程启动之后锁定了调用循环。在.NET中,最好使用Task进行多任务处理(例如,如果需要用户界面访问,可以将它们称为Task.Run(async delegate() { await yourTask(); })(或AsyncContext.Run(...)),并且不会阻塞任何内容。
  • 整个GetInformationFromWeb类目前是没有意义的,而您也在无意义地生成多个客户机对象,因为一个HTTP客户机对象可以处理多个请求(如果您使用HttpClient,即使没有额外的膨胀,您也只需将其实例化一次,作为具有所有必要配置的静态全局变量,然后调用它。m任何使用像client.GetStringAsync(Uri uri)那样少代码的地方。
  • OT:这是一个学术项目吗?


    如果您的foreach循环了500多家公司,并且每个公司都在创建一个新的线程,那么您的互联网速度可能会成为瓶颈,您将收到超过6秒的超时,并且经常失败。

    我建议你尝试并行。注意MaxDegreeOfParallelism,它设置了并行执行的最大数量。你可以根据你的需要调整这个。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     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
                    }
                });