2015-05-11 9 views
5

Ich versuche, eine Anwendung zu erstellen, die Multi-Thread-Downloads Bilder von einer Website, als eine Einführung in Threading. (nie zuvor richtig Threading verwendet)Wo kommen diese 1k Threads von

Aber im Moment scheint es 1000+ Threads zu erstellen und ich bin mir nicht sicher, woher sie kommen.

ich zum ersten Mal eines Thread in einen Thread-Pool Warteschlange, für den Anfang, ich habe nur 1 Job im Job Array

foreach (Job j in Jobs) 
{ 
    ThreadPool.QueueUserWorkItem(Download, j); 
} 

, die die Leere Download(object obj) auf einen neuen Thread beginnt, wo es durch eine bestimmte Anzahl von Seiten Schleifen (Bilder benötigt/42 Bilder pro Seite)

for (var i = 0; i < pages; i++) 
{ 
    var downloadLink = new System.Uri("http://www." + j.Provider.ToString() + "/index.php?page=post&s=list&tags=" + j.Tags + "&pid=" + i * 42); 

    using (var wc = new WebClient()) 
    { 
     try 
     { 
      wc.DownloadStringAsync(downloadLink); 
      wc.DownloadStringCompleted += (sender, e) => 
      { 
       response = e.Result; 
       ProcessPage(response, false, j); 
      }; 
     } 
     catch (System.Exception e) 
     { 
      // Unity editor equivalent of console.writeline 
      Debug.Log(e); 
     } 
    } 
} 

korrigiert mich wenn ich falsch bin, wird die nächste Lücke wird auf dem gleichen Thread genannt

void ProcessPage(string response, bool secondPass, Job j) 
{ 
    var wc = new WebClient(); 
    LinkItem[] linkResponse = LinkFinder.Find(response).ToArray(); 

    foreach (LinkItem i in linkResponse) 
    { 
     if (secondPass) 
     { 
      if (string.IsNullOrEmpty(i.Href)) 
       continue; 
      else if (i.Href.Contains("http://loreipsum.")) 
      { 
       if (DownloadImage(i.Href, ID(i.Href))) 
        j.Downloaded++; 
      } 
     } 
     else 
     { 
      if (i.Href.Contains(";id=")) 
      { 
       var alterResponse = wc.DownloadString("http://www." + j.Provider.ToString() + "/index.php?page=post&s=view&id=" + ID(i.Href)); 
       ProcessPage(alterResponse, true, j); 
      } 
     } 
    } 
} 

und gelangt schließlich in die letzte Funktion auf und lädt das aktuelle Bild

bool DownloadImage(string target, int id) 
{ 
    var url = new System.Uri(target); 
    var fi = new System.IO.FileInfo(url.AbsolutePath); 
    var ext = fi.Extension; 

    if (!string.IsNullOrEmpty(ext)) 
    { 
     using (var wc = new WebClient()) 
     { 
      try 
      { 
       wc.DownloadFileAsync(url, id + ext); 
       return true; 
      } 
      catch(System.Exception e) 
      { 
       if (DEBUG) Debug.Log(e); 
      } 
     } 
    } 
    else 
    { 
     Debug.Log("Returned Without a extension: " + url + " || " + fi.FullName); 
     return false; 
    } 
    return true; 
} 

Ich bin nicht sicher, wie ich so viele Threads bin ab, würde aber gerne wissen.

bearbeiten

Das Ziel dieses Programms ist es, die verschiedenen Jobs in Jobs zur gleichen Zeit (maximal 5), das jeweils das Herunterladen eines maximal 42 Bilder zur Zeit zum Download bereit.

so können maximal 210 Bilder zu jeder Zeit maximal heruntergeladen werden.

+4

Sie führen asynchrone Operation in einem anderen Thread aus. Warum verwenden Sie nicht nur async und das ist. ? Welche Vorteile bietet Ihnen Threading in diesem Fall? – Tigran

+0

@Tigran Wahrscheinlich keine, nur versucht, das Gewinde des Threads zu bekommen, wäre es sinnvoller, den blockierenden Aufruf anstelle des asynchronen Aufrufs dann in einer Threading-Situation zu verwenden? –

+2

Wenn Sie Async verwenden, verwenden Sie kein Threading. Verwenden Sie Threads, wenn Sie die Arbeitslast für Parallelität steuern möchten. Überspannen Sie so viele Threads wie erforderlich und nicht mehr. – Tigran

Antwort

2

Zuerst, wie haben Sie die Fadenzahl gemessen? Warum denken Sie, dass Sie tausend von ihnen in Ihrer Bewerbung haben? Sie verwenden die ThreadPool, so dass Sie sie nicht selbst erstellen, und die ThreadPool würde nicht so viele von ihnen für ihre Bedürfnisse erstellen.

Zweitens mischen Sie synchrone und asynchrone Operationen in Ihrem Code. Da Sie TPL und async/await nicht verwenden können, gehen wir durch Ihren Code und zählen die unit-of-works, die Sie erstellen, so dass Sie sie minimieren können. Nachdem Sie dies getan haben, wird die Anzahl der in der Warteschlange befindlichen Elemente in ThreadPool sinken und Ihre Anwendung wird die Leistung erhalten, die Sie benötigen.

  1. Sie setzen nicht die SetMaxThreads Methode in der Anwendung, so, according the MSDN:

    Maximale Anzahl der Thread-Pool Themen
    Die Anzahl der Operationen, die an den Thread die Warteschlange gestellt werden können Pool ist nur durch verfügbaren Speicher begrenzt; jedoch begrenzt der Thread-Pool die Anzahl der Threads, die gleichzeitig im Prozess aktiv sein können .Standardmäßig ist das Limit auf 25 Worker-Threads pro CPU und 1.000 E/A-Fertigstellungsthreads festgelegt.

    Sie müssen also das Maximum auf 5 setzen.

  2. Ich kann keinen Platz in Ihrem Code finden, wo Sie die 42 Bilder pro Job überprüfen, Sie erhöhen nur den Wert in ProcessPage Methode.

  3. Überprüfen Sie die ManagedThreadId für das Handle von WebClient.DownloadStringCompleted - führt es in anderen Thread oder nicht.
  4. Sie fügen das neue Element in ThreadPool Warteschlange hinzu, warum verwenden Sie die asynchrone Operation für das Herunterladen? Verwenden Sie ein synchronious overload, wie folgt aus:

    ProcessPage(wc.DownloadString(downloadLink), false, j); 
    

    Dies wird nicht schaffen ein anderes Element in ThreadPool Warteschlange, und Sie würden nicht einen sinchronisation Kontextwechsel hier haben.

  5. In ProcessPage Ihre wc Variable wird nicht Müll gesammelt, so dass Sie nicht alle Ihre Ressourcen hier freigeben. In using Aussage hier:

    void ProcessPage(string response, bool secondPass, Job j) 
    { 
        using (var wc = new WebClient()) 
        { 
         LinkItem[] linkResponse = LinkFinder.Find(response).ToArray(); 
    
         foreach (LinkItem i in linkResponse) 
         { 
          if (secondPass) 
          { 
           if (string.IsNullOrEmpty(i.Href)) 
            continue; 
           else if (i.Href.Contains("http://loreipsum.")) 
           { 
            if (DownloadImage(i.Href, ID(i.Href))) 
             j.Downloaded++; 
           } 
          } 
          else 
          { 
           if (i.Href.Contains(";id=")) 
           { 
            var alterResponse = wc.DownloadString("http://www." + j.Provider.ToString() + "/index.php?page=post&s=view&id=" + ID(i.Href)); 
            ProcessPage(alterResponse, true, j); 
           } 
          } 
         } 
        } 
    } 
    
  6. In DownloadImage Methode Sie auch die asynchronious Last verwenden. Dies fügt auch Artikel in ThreadPoll Warteschlange, und ich denke, dass Sie dies vermeiden können, und verwenden Sie synchronious overload auch:

    wc.DownloadFile(url, id + ext); 
    return true; 
    

So im Allgemeinen die kontextSchaltVorgänge zu vermeiden und Ihre Ressourcen ordnungsgemäß entsorgen.

0

Ihr wc WebClinet wird außerhalb des Gültigkeitsbereichs liegen und vor dem asynchronen Rückruf per Zufallsmeldung gesammelt werden. Außerdem müssen Sie bei allen asynchronen Aufrufen eine sofortige Rückgabe und die Rückgabe der tatsächlichen delegierten Funktion zulassen. Daher muss sich processPage an zwei Stellen befinden. Je nachdem, wo der Download in der Originalschleife deklariert ist, kann auch das j in der Originalschleife den Gültigkeitsbereich verlassen.