1

我正在尝试制作一个多线程网络刮板。为此,我正在使用 Parallel.For。这是我的代码:

string source = "http://www.lolsummoners.com/ladders/eune/";
        string regex_Search = "leagues/.*>(.*)</a></td>";
        List<string> user_List = new List<string>();
        int page_Begin = 1;
        int page_End = 10;

        ParallelOptions p_Option = new ParallelOptions();
        p_Option.MaxDegreeOfParallelism = 3;
        Parallel.For(page_Begin, page_End, p_Option, index =>
            {
                try
                {
                    WebClient web = new WebClient();
                    web.Encoding = Encoding.UTF8;
                    String html = web.DownloadString(source + page_Begin);
                    MatchCollection matches = Regex.Matches(html, @regex_Search);
                    foreach(Match match_Find in matches)
                    {
                        string user = match_Find.Groups[1].Value.Replace(" ", string.Empty);
                        user_List.Add(user);
                        Console.WriteLine(user);
                    }
                }
                catch(Exception e)
                {
                    Console.WriteLine(e.Message);
                }
                page_Begin++;
            });
        Console.ReadKey();

我的问题是,如果我使用多个线程,我会得到重复。有没有办法解决这个问题?我不需要循环从同一个网页获取相同的名称,这就是为什么我增加 page_Begin 变量结束。这就是我要说的: 重复问题

4

1 回答 1

1

您看到重复项是因为您没有使用当前并行传递的索引。相反,您使用的是之前定义的。因此,在至少两个线程的两次传递中,两者都将开始下载!随着增量,后续通行证将下载副本。page_Begin1

更改此行:

String html = web.DownloadString(source + page_Begin);

...到

String html = web.DownloadString(source + index );

并发问题

List<>不是线程安全的,因此您可能希望使用来自 TPL 的不错的线程安全集合,例如ConcurrentBag<>.

MSDN:

当订购无关紧要时,包对于存储物品很有用,而且与集合不同,包支持重复。ConcurrentBag 是一个线程安全的包实现,针对同一线程将同时生产和使用包中存储的数据的场景进行了优化。

中定义的集合System.Collections.Concurrent都非常有效,并且比通常用lock(object).

改变这个:

List<string> user_List = new List<string>();

...到:

ConcurrentBag<string> user_List = new ConcurrentBag<string>();

现在您可以从任何线程添加到包中。

稍后当您完成线程时,您可以将其转换为带有.ToArray().

于 2016-01-17T13:19:26.643 回答