1

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

一开始只是对当前顺序操作集的使用的简单想法TPL导致我停止了我的项目。

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

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

每天有多个请求(大约 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;
            }
        }

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

4

1 回答 1

3

我至少看到几个问题:

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

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

  3. 您正在从不同的线程写入同一个文件(好吧,至少我认为这是您的意图) - 通常这是多线程灾难的秘诀。问题是,如果您根本需要将任何内容写入磁盘,或者可以将其作为字符串下载并在不接触磁盘的情况下进行解析 -有一个很好的例子是什么意思 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;
    }
}
于 2012-11-22T14:40:39.377 回答