【问题标题】:Retrieve a string containing html Document source using Task parallel使用任务并行检索包含 html 文档源的字符串
【发布时间】:2012-11-22 14:32:19
【问题描述】:

我真的希望有人对 TPLSystem.Net 类和方法都有足够的经验

最初只是想在当前的一系列操作上使用TPL,这导致我的项目停止了。

由于我对 .NET 还很陌生,所以使用 TPL 直接跳到深水区……

我试图使用 WebClient 提取 Aspx 页面的源/内容(html)

每天有多个请求(大约 20-30 页要通过)并从源代码中提取特定值......这只是服务器在其列表中的少数日常任务之一,

引导我尝试使用 TPL 来实现它,从而获得一些速度。

虽然我尝试使用 Task.Factory.StartNew() 尝试迭代几个 WC 实例, 第一次尝试执行 WC 时,应用程序没有从 WebClient 获得任何结果

这是我最后一次尝试

    static void Main(string[] args)
    {
        EnumForEach<Act>(Execute);
        Task.WaitAll();
    }

    public static void EnumForEach<Mode>(Action<Mode> Exec)
    {
            foreach (Mode mode in Enum.GetValues(typeof(Mode)))
            {
                Mode Curr = mode;

                Task.Factory.StartNew(() => Exec(Curr) );
            }
    }

    string ResultsDirectory = Environment.CurrentDirectory,
        URL = "",
        TempSourceDocExcracted ="",
        ResultFile="";

        enum Act
        {
            dolar, ValidateTimeOut
        }

    void Execute(Act Exc)
    {
        switch (Exc)
        {
            case Act.dolar:
                URL = "http://www.AnyDomainHere.Com";
                ResultFile =ResultsDirectory + "\\TempHtm.htm";
                TempSourceDocExcracted = IeNgn.AgilityPacDocExtraction(URL).GetElementbyId("Dv_Main").InnerHtml;
                File.WriteAllText(ResultFile, TempSourceDocExcracted);
                break;
            case Act.ValidateTimeOut:
                URL = "http://www.AnotherDomainHere.Com";
                ResultFile += "\\TempHtm.htm";
                TempSourceDocExcracted = IeNgn.AgilityPacDocExtraction(URL).GetElementbyId("Dv_Main").InnerHtml;
                File.WriteAllText(ResultFile, TempSourceDocExcracted);
                break;
        }

        //usage of HtmlAgilityPack to extract Values of elements by their attributes/properties
        public HtmlAgilityPack.HtmlDocument AgilityPacDocExtraction(string URL)
        {
            using (WC = new WebClient())
            {
                WC.Proxy = null;
                WC.Encoding = Encoding.GetEncoding("UTF-8");
                tmpExtractedPageValue = WC.DownloadString(URL);
                retAglPacHtmDoc.LoadHtml(tmpExtractedPageValue);
                return retAglPacHtmDoc;
            }
        }

我做错了什么?是否可以使用 TPL 使用 WebClient 或者我应该使用其他工具(无法使用 IIS 7 / .net4.5)?

【问题讨论】:

    标签: c# .net-4.0 task-parallel-library system.net


    【解决方案1】:

    我至少看到了几个问题:

    1. 命名 - FlNm 不是名称 - VisualStudio 是具有智能代码完成功能的现代 IDE,无需保存击键(您可以从这里开始,也有其他选择,主要是保持一致: C# Coding Conventions.

    2. 如果您使用多线程,则需要注意资源共享。例如 FlNm 是一个静态字符串,它是在每个线程内分配的,所以它的值不是确定性的(即使它是按顺序运行的,代码也会出错 - 你会在每次迭代中在路径中添加文件名,所以它会是比如 c:\TempHtm.htm\TempHtm.htm\TempHtm.htm)

    3. 您正在从不同的线程写入同一个文件(嗯,至少我认为这是您的意图)——这通常是多线程灾难的根源。问题是,如果您需要将任何内容写入磁盘,或者可以将其作为字符串下载并在不接触磁盘的情况下进行解析 - there's a good example what does it mean to touch a disk

    4. 总的来说,我认为你应该只并行下载,所以不要在多线程中涉及 HtmlAgilityPack,因为我认为你不知道它是线程安全的。另一方面,下载将具有良好的性能/线程数比率,html 解析 - 不是那么多,如果线程数等于内核数,但不会更多。甚至更多——我会将下载和解析分开,因为这样更容易测试、理解和维护。

    更新:我不明白你的全部意图,但这可能会帮助你开始(它不是生产代码,你应该添加重试/错误捕获等)。 最后还有扩展的 WebClient 类,允许您获得更多的线程,因为默认情况下 webclient 只允许两个连接。

    class Program
    {
        static void Main(string[] args)
        {
            var urlList = new List<string>
                              {
                                  "http://google.com",
                                  "http://yahoo.com",
                                  "http://bing.com",
                                  "http://ask.com"
                              };
    
            var htmlDictionary = new ConcurrentDictionary<string, string>();
            Parallel.ForEach(urlList, new ParallelOptions { MaxDegreeOfParallelism = 20 }, url => Download(url, htmlDictionary));
            foreach (var pair in htmlDictionary)
            {
                Process(pair);
            }
        }
    
        private static void Process(KeyValuePair<string, string> pair)
        {
            // do the html processing
        }
    
        private static void Download(string url, ConcurrentDictionary<string, string> htmlDictionary)
        {
            using (var webClient = new SmartWebClient())
            {
                htmlDictionary.TryAdd(url, webClient.DownloadString(url));
            }
        }
    }
    
    public class SmartWebClient : WebClient
    {
        private readonly int maxConcurentConnectionCount;
    
        public SmartWebClient(int maxConcurentConnectionCount = 20)
        {
            this.maxConcurentConnectionCount = maxConcurentConnectionCount;
        }
    
        protected override WebRequest GetWebRequest(Uri address)
        {
            var httpWebRequest = (HttpWebRequest)base.GetWebRequest(address);
            if (httpWebRequest == null)
            {
                return null;
            }
    
            if (maxConcurentConnectionCount != 0)
            {
                httpWebRequest.ServicePoint.ConnectionLimit = maxConcurentConnectionCount;
            }
    
            return httpWebRequest;
        }
    }
    

    【讨论】:

    • 当我启动当前应用程序(使用控制台应用程序)时,主要方法是静态的,所以为了使执行的方法及其变量是非静态的,我应该使用一个单独的 cals 和 instenciatet 它。 (解决您列表中的第 2 个问题?)
    • 好吧,就算不是静态的,如果多线程修改,也是有问题的。我建议使用一些并发集合来保存线程的结果。
    • 是的,我会的,最后一个代码版本将在dictionary &lt;title of code, its value&gt; 中收集,尽管WebClient 的实例(多个...10 和...并行)需要一些时间才能开始起来..并得到结果。这是第一次停止,因为我开始遇到问题
    • 我已经用简单的示例更新了答案,希望它能让你开始,因为坦率地说,从你的代码中很难理解你想要做什么以及你需要做什么。跨度>
    • 哇,这看起来很棒,因为我对那里发生的事情一无所知,我将测试每个部分,我会在此回复您,谢谢一百万!新项目是 GiedriusParralelSmartWC
    猜你喜欢
    • 2014-12-02
    • 2015-10-12
    • 2021-07-06
    • 1970-01-01
    • 2020-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多