C# 在解析时同时启动所有线程

C# Starting all thread in the same time while parsing

我目前正在学习 C#,过去两天我一直在研究 XML 解析器。它实际上工作正常我的问题是解析超过 10k 页所花费的时间。这是我的代码。

    public static void startParse(int id_min, int id_max, int numberofthreads)
    {
        int start;
        int end;
        int part;
        int threadnbrs;

        threadnbrs = numberofthreads;
        List<Thread> workerThreads;
        List<string> results;

        part = (id_max - id_min) / threadnbrs;
        start = id_min;
        end = 0;
        workerThreads = new List<Thread>();
        results = new List<string>();

        for (int i = 0; i < threadnbrs; i++)
        {
            if (i != 0)
                start = end + 1;
            end = start + (part);
            if (i == (threadnbrs - 1))
                end = id_max;

            int _i = i;
            int _start = start;
            int _end = end;

            Thread t = new Thread(() =>
            {

                   Console.WriteLine("i = " + _i);
                   Console.WriteLine("start =" + _start);
                   Console.WriteLine("end =" + _end + "\r\n");
                   string parse = new ParseWH().parse(_start, _end);
                   lock (results)
                   {
                       results.Add(parse);
                   }
            });
            workerThreads.Add(t);
            t.Start();
        }
        foreach (Thread thread in workerThreads)
               thread.Join();

        File.WriteAllText(".\result.txt", String.Join("", results));
        Console.Beep();
    }

我实际上在做的是在不同线程中拆分需要解析的一系列元素,以便每个线程处理 X 个元素。

每 100 个元素大约需要 20 秒。 然而我花了 17 分钟来解析 10 0000 个元素。

我需要的是每个线程同时处理这 10 000 个元素中的 100 个,这样它可以在 20 秒内完成。有解决办法吗?

解析代码:

public string parse(int id_min, int id_max)
        {
            XmlDocument xml;
            WebClient user;
            XmlElement element;
            XmlNodeList nodes;
            string result;
            string address;
            int i;

            //Console.WriteLine(id_min);
            //Console.WriteLine(id_max);
            i = id_min;
            result = "";
            xml = new XmlDocument();
            while (i <= id_max)
            {
                user = new WebClient();
                // user.Headers.Add("User-Agent", "Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");
                user.Encoding = UTF8Encoding.UTF8;
                address = "http://fr.wowhead.com/item=" + i + "?xml";
                if (address != null)
                    xml.LoadXml(user.DownloadString(new Uri(address)));
                element = xml.DocumentElement;
                nodes = element.SelectNodes("/wowhead");
                if (xml.SelectSingleNode("/wowhead/error") != null)
                {
                    Console.WriteLine("error " + i);
                    i++;
                    continue;
                }
                result += "INSERT INTO item_wh (entry, class, subclass, displayId, ,quality, name, level) VALUES (";
                foreach (XmlNode node in nodes)
                {
                    // entry
                    result += node["item"].Attributes["id"].InnerText;
                    result += ", ";
                    // class
                    result += node["item"]["class"].Attributes["id"].InnerText;
                    result += ", ";
                    // subclass
                    result += node["item"]["subclass"].Attributes["id"].InnerText;
                    result += ", ";
                    // displayId
                    result += node["item"]["icon"].Attributes["displayId"].InnerText;
                    result += ", ";
                    // quality
                    result += node["item"]["quality"].Attributes["id"].InnerText;
                    result += ", \"";
                    // name
                    result += node["item"]["name"].InnerText;
                    result += "\", ";
                    // level
                    result += node["item"]["level"].InnerText;
                    result += ");";
                    // bakcline
                    result += "\r\n";
                }
                i++;
            }
            return (result);
        }

CPU 绑定工作(例如解析)的最佳解决方案是启动与机器中核心数量一样多的线程,少于此数量并且您没有利用所有核心,不止于此,而且过度 context-switching 可能会影响性能。

所以基本上,threadnbrs 应该设置为 Environment.ProcessorCount

此外,考虑使用并行 class 而不是自己创建线程:

Parallel.ForEach(thingsToParse, (somethingToParse) =>
        {
            var parsed = Parse(somethingToParse);
            results.Add(parsed);
        });

您必须同意它看起来更干净,更易于维护。 此外,您最好使用 ConcurrentBag 而不是常规的 List + lock,因为 ConcurrentBag 更适合并发加载,并且可以为您提供更好的性能。

终于!通过同时启动我的应用程序的多个进程让它工作。

表示如果我在 1000 个元素的 10 个过程中有 10 k 个元素 运行。增加流程数量以减少元素数量,并且速度越来越快! (我目前 运行 正在以非常快的互联网速度上网)并拥有三星 M.2 960 作为存储以及核心 I7 Skylake 6 核

好吧,我找到了“”,它叫做"Thread Pool"我最终决定直接下载XML文件然后直接离线解析文档,而不是直接解析网站到获得 SQL 格式。新方法奏效了,我可以在 9 秒内下载和写入多达 10 000 K XML。我试图将它推到 150 K(所有网站页面),但现在我遇到了一个奇怪的错误,我得到了重复的项目...我将尝试使用正确的池方法重写完整代码,multi Task/Thread, dictionary 和 IEnumerable Containers 交叉手指处理 150 k Item 而不会丢失处理中的数据并且 post 返回完整代码。