2017-01-29 3 views
0

Ich lerne gerade C# und ich habe in den letzten zwei Tagen an einem XML-Parser gearbeitet. Es funktioniert wirklich gut mein Problem ist die Zeit, die es dauert, mehr als 10k Seiten zu parsen. Das ist mein Code.C# Starten aller Threads in der gleichen Zeit beim Parsen

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(); 
    } 

was ich eigentlich tun Splitting ein Bereich des Elements in verschiedenen Thread, der syntaktisch analysiert werden müssen, so dass jeder Thread-Handle X-Elemente.

für jeweils 100 Elemente dauert es ca. 20 Sekunden. aber ich brauchte 17 Minuten, um 10 0000 Elemente zu parsen.

Was ich brauche, ist jeder Thread, der gleichzeitig an 100 dieser 10 000 Elemente arbeitet, so dass es in 20 Sekunden erledigt werden kann. Gibt es dafür eine Lösung?

Parse-Code:

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); 
     } 
+3

So dauert es 20 Sekunden, 100 Elemente zu analysieren ... wie erwarten Sie 1000x den Durchsatz zu erreichen, 1000 mal so viele Elemente in der gleichen Zeit zu analysieren? Threading bietet nicht nur auf magische Weise freie Rechenleistung oder Netzwerkbandbreite (wir haben keine Ahnung, was die Zeit für die 100 Elemente braucht). –

+0

@JonSkeet - aber das Hinzufügen von Threads ermöglicht es ihnen, auf mehrere CPU-Kerne zuzugreifen, während lineares Arbeiten nur einen Kern maximal verwendet. Auf einer Multi-Core-Maschine korrelliert Threading mehr Kerne in das Verfahren. Stimme vollständig dem Problem hier ist die Zeit, um 100 Elemente zu analysieren, die massiv übermäßig ist. – PhillipH

+1

@PhillipH Auf einem typischen modernen Computer mit, sagen wir, 8 Kernen, würden Sie maximal 7-8x beschleunigen, nicht 1000x. Als Nebenbemerkung werden andere Dinge schnell zum Engpass (wie Netzwerk- oder Festplatten-IO). Sie sollten besser Ihren Code profilieren, um herauszufinden, warum Ihre Analyse so lange dauert, und das stattdessen beheben. –

Antwort

0

Okay, so fand ich "Trying to run multiple HTTP requests in parallel, but being limited by Windows (registry)" es heißt "Thread Pool" ich Schließlich entschied man sich, die XML-Datei direkt herunterzuladen und dann das Dokument direkt offline zu parsen, anstatt die Website direkt zu analysieren, um ein SQL-Format zu erhalten. Die neue Methode funktioniert, ich kann herunterladen und schreiben bis zu 10 000 K XML in nur 9 Sekunden. Ich habe versucht, es auf 150 K (Alle Websites Seiten) zu schieben, aber jetzt habe ich einen seltsamen Fehler Ich habe doppelte Einträge ... Ich werde versuchen, den vollen Code mit der richtigen Methode für Pools, Multi Task/Thread neu zu schreiben, Dictionary und IEnumerable Containers kreuzen den Finger, um an 150 k Item zu arbeiten, ohne Daten im Prozess zu verlieren und den vollen Code zurückzuschicken.

0

Beste Lösung für die CPU-gebundene Arbeit (wie Parsing) ist, so viele Threads wie die Anzahl der Kerne in Ihrer Maschine zu starten, weniger als das, und Sie sind nicht unter Ausnutzung von all Ihren Kernen, mehr als das und exzessive Kontextwechsel können die Leistung beeinträchtigen.

So im Wesentlichen, threadnbrs sollte die Parallel-Klasse statt der Schaffung Fäden selbst

auch auf Environment.ProcessorCount eingestellt werden, berücksichtigen:

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

Sie bestätigen müssen, dass es viel sauberer und viel einfacher aussieht aufrecht erhalten. Außerdem ist es besser, wenn Sie ConcurrentBag anstelle einer regulären List + -Sperre verwenden, da ConcurrentBag eher für gleichzeitige Ladevorgänge ausgelegt ist und Ihnen eine bessere Leistung bieten kann.

0

Endlich! Habe es geschafft, indem ich mehrere Prozesse meiner Anwendung gleichzeitig gestartet habe.

Hexe bedeutet, wenn ich 10 k Elemente habe, laufe ich in 10 Prozess von 1000 Elementen. Erhöhen Sie die Anzahl der Prozesse, um die Anzahl der Elemente zu verringern, und es geht immer schneller! (Ich bin derzeit auf einer sehr schnellen Internet Speed) und habe ein Samsung M.2 960 als Speicher sowie Kern I7 Skylake 6 Kerne

Verwandte Themen