2015-05-11 29 views
5

Próbuję utworzyć aplikację, która pobiera wiele wątków ze strony internetowej, jako wprowadzenie do wątków. (nigdy wcześniej nie używałem poprawnie gwintowania)Skąd te wątki 1k pochodzą z

Ale obecnie wydaje się, że tworzymy ponad 1000 wątków i nie jestem pewien, skąd pochodzą.

raz pierwszy w kolejce wątku w puli wątków, na początek mam tylko 1 pracę w tablicy zatrudnienia

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

który rozpoczyna pustkę Download(object obj) w nowym wątku, gdzie pętle za pośrednictwem pewnej ilości stron (obrazy potrzebne/42 obrazów na stronie)

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

mnie poprawić, jeśli się mylę, kolejna pustka zostanie wywołany w tym samym wątku

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

I wreszcie przechodzi do ostatniej funkcji i pobiera rzeczywisty obraz

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

Nie jestem pewien, jak ja zaczynam to wiele wątków, ale chcielibyśmy wiedzieć.

Edit

Celem tego programu jest, aby pobrać inną pracę w pracy w tym samym czasie (max 5) każdy pobierany maksymalnie 42 zdjęć na raz.

, aby maksymalnie 210 zdjęć można było/powinno się pobrać maksymalnie przez cały czas.

+4

uruchamiasz operację asynchroniczną wewnątrz innego wątku. dlaczego po prostu nie używać async i to jest. ? jakie korzyści zapewnia Tobie w tym przypadku? – Tigran

+0

@ Tigran Prawdopodobnie żaden, po prostu próbując uzyskać powolny wątek, czy byłoby sensowniej użyć wywołania blokującego zamiast wywołania asynchronicznego, a następnie w sytuacji wątku? –

+2

Jeśli używasz asynchronizacji, nie używaj wątków. używaj wątków, jeśli zamierzasz kontrolować obciążenie pracą współbieżności, tak aby obejmował tyle wątków, ile jest wymagane, a nie więcej. – Tigran

Odpowiedz

2

Po pierwsze, w jaki sposób zmierzyłeś liczbę wątków? Jak myślisz, dlaczego masz ich tysiąc w swojej aplikacji? Używasz ThreadPool, więc nie tworzysz ich samemu, a ThreadPool nie stworzy tak dużej ilości ich dla swoich potrzeb.

Po drugie, miksujesz synchroniczne i asynchroniczne operacje w swoim kodzie. Ponieważ nie możesz użyć TPL i async/await, przejdźmy przez twój kod i policzmy unit-of-works, który tworzysz, abyś mógł je zminimalizować. Po wykonaniu tej czynności liczba elementów oczekujących w kolejce w numerze ThreadPool zostanie zmniejszona, a aplikacja uzyska pożądaną wydajność.

  1. Nie ustawić metodę SetMaxThreads w aplikacji, tak, according the MSDN:

    Maksymalna liczba wątku Pool Nici
    liczba operacji, które mogą być ustawiane w kolejce do wątku Pula jest ograniczona tylko dostępną pamięcią; Jednak pula wątków ogranicza liczbę wątków, które mogą być aktywne w procesie jednocześnie.Domyślnie limit wynosi 25 wątków roboczych na procesor i 1000 wątków zakończenia we/wy.

    Musisz więc ustawić maksimum na 5.

  2. Nie mogę znaleźć miejsca w kodzie, w którym sprawdzasz obrazy 42 dla zadania, tylko zwiększasz wartość w metodzie ProcessPage.

  3. Sprawdź ManagedThreadId dla rączki WebClient.DownloadStringCompleted - czy jest wykonywany w innym wątku, czy nie.
  4. Dodajesz nową pozycję w kolejce ThreadPool, dlaczego używasz operacji asynchronicznej do pobierania? Użyj synchronious overload coś takiego:

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

    To nie stworzy kolejną jedną pozycję w ThreadPool kolejce, a nie miałoby przełącznik kontekstowe sinchronisation tutaj.

  5. W ProcessPage zmienna wc nie są zbierane śmieci, więc nie zwalniając wszystkich resourses tutaj. Dodaj using oświadczenie tutaj:

    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. W DownloadImage metoda również użyć asynchronious obciążenia. Dodaje to również element ThreadPoll kolejce, i myślę, że można tego uniknąć i używać synchronious overload też:

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

Tak w ogóle uniknąć operacji przełączania kontekstu i dysponowania swoimi zasobami prawidłowo.

0

Twoja toaleta WebClinet wyjdzie poza zakres i zostanie losowo wyrzucona do śmieci przed wywołaniem asynchronicznym. Również na wszystkich połączeniach asynchronicznych należy zezwolić na natychmiastowy powrót, a faktyczna delegowana funkcja zwraca. Więc processPage będzie musiała być w dwóch miejscach. Również j w oryginalnej pętli może wychodzić poza zakres w zależności od miejsca zadeklarowanego przez Download w oryginalnej pętli.