2012-05-24 23 views
6

Czy są jakieś zmiany, które wielu pracowników tła wykonuje lepiej niż zadania w 5-sekundowych uruchomionych procesach? Pamiętam, jak czytałem w książce, że zadanie jest przeznaczone dla krótkich procesów.zadanie pracownik tła C#

reasong pytam to:

Mam procesu, który trwa 5 sekund, aby zakończyć, i istnieje 4000 zakończenie procesów. Na początku robiłam:

for (int i=0; i<4000; i++) { 
    Task.Factory.StartNewTask(action); 
} 

i ten miał słabe wyniki (po pierwszej minucie, gdzie 3-4 zadania zakończone, a aplikacja konsoli miał 35 wątków). Może to było głupie, ale pomyślałem, że pula wątków poradzi sobie z taką sytuacją (wszystkie akcje zostaną umieszczone w kolejce, a gdy wątek jest wolny, wykona akcję i wykona ją).

Drugim krokiem było ręczne wykonanie zadań dla środowiska Environment.ProcessorCount i wszystkich akcji, które należy umieścić w ConcurentQueue. Kod będzie wyglądał mniej więcej tak:

var workers = new List<BackgroundWorker>(); 
//initialize workers 

workers.ForEach((bk) => 
{ 
    bk.DoWork += (s, e) => 
    { 
     while (toDoActions.Count > 0) 
     { 
      Action a; 
      if (toDoActions.TryDequeue(out a)) 
      { 
       a(); 
      } 
     } 
    } 

    bk.RunWorkerAsync(); 
}); 

To było lepsze. Wykonał znacznie lepiej niż zadania, nawet gdy miałem 30 pracowników w tle (tyle zadań, co w pierwszym przypadku).

LE:

zacznę Zadania tak:

public static Task IndexFile(string file) 
    { 
     Action<object> indexAction = new Action<object>((f) => 
     { 
      Index((string)f); 
     }); 

     return Task.Factory.StartNew(indexAction, file); 
    } 

A metoda Index jest to jedno:

private static void Index(string file) 
    { 
     AudioDetectionServiceReference.AudioDetectionServiceClient client = new AudioDetectionServiceReference.AudioDetectionServiceClient(); 

     client.IndexCompleted += (s, e) => 
      { 
       if (e.Error != null) 
       { 
        if (FileError != null) 
        { 
         FileError(client, 
          new FileIndexErrorEventArgs((string)e.UserState, e.Error)); 
        } 
       } 
       else 
       { 
        if (FileIndexed != null) 
        { 
         FileIndexed(client, new FileIndexedEventArgs((string)e.UserState)); 
        } 
       } 
      }; 

     using (IAudio proxy = new BassProxy()) 
     { 
      List<int> max = new List<int>(); 
      if (proxy.ReadFFTData(file, out max)) 
      { 
       while (max.Count > 0 && max.First() == 0) 
       { 
        max.RemoveAt(0); 
       } 
       while (max.Count > 0 && max.Last() == 0) 
       { 
        max.RemoveAt(max.Count - 1); 
       } 

       client.IndexAsync(max.ToArray(), file, file); 
      } 
      else 
      { 
       throw new CouldNotIndexException(file, "The audio proxy did not return any data for this file."); 
      } 
     } 
    } 

Metoda ta odczytuje z pliku mp3 niektóre dane, używając biblioteka Bass.net. Następnie dane są wysyłane do usługi WCF, przy użyciu metody asynchronicznej. Metoda IndexFile (ciąg plików), która tworzy zadania, jest wywoływana 4000 razy w pętli for. Te dwa zdarzenia, FileIndexed i FileError nie są obsługiwane, więc nie są nigdy zgłaszane.

+0

Możesz użyć 'BlockingCollection' zamiast' ConcurrentQueue' (użyje on 'ConcurrentQueue' wewnętrznie). Sprawi, że kod będzie nieco czystszy i łatwiejszy w użyciu. – Servy

+0

Dzięki za podpowiedź ... Zmienię :) –

+0

Czy próbowałeś 'Parallel.Invoke' z szeregiem działań? – mellamokb

Odpowiedz

1

Przyczyną, dla której wydajność zadania była tak niska, było to, że zainstalowano zbyt wiele małych zadań (4000). Pamiętaj, że procesor musi również zaplanować zadania, więc montaż wielu krótkotrwałych zadań powoduje dodatkowe obciążenie pracą procesora. Więcej informacji można znaleźć w drugim akapicie TPL:

Począwszy Framework 4, OC jest preferowanym sposobem zapisu wielowątkowy i kodu równoległego. Jednak nie cały kod jest odpowiedni do zrównoleglania; na przykład, jeśli pętla wykonuje tylko niewielką ilość pracy w każdej iteracji lub nie działa dla wielu iteracji , to narzut związany z równoległością może spowodować, że kod będzie wolniej pracować z kodem .

Gdy używany robotników w tle, to ogranicza liczbę możliwych żywych wątków do ProcessCount. Co zmniejszyło wiele kosztów związanych z planowaniem.

0

Czy rozważałeś użycie wątku?

http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx

Jeśli wydajność jest mniejsza przy stosowaniu nici mogą być tylko ze względu na gwintowanie górny (przydzielania i zniszczenia poszczególnych nici).

+0

Cóż, zadania są pobierane z ThreadPool? Tworzę obiekty zadań, a nie wątki. –

+0

Już korzystał z TP, to była skarga. –

+1

Zadanie korzysta z wątku. –

1

Biorąc pod uwagę, że masz ściśle zdefiniowaną listę rzeczy do zrobienia, użyłbym klasy Parallel (albo For lub ForEach w zależności od tego, co bardziej Ci odpowiada). Ponadto można przekazać parametr konfiguracyjny do żadnej z tych metod, aby kontrolować, ile zadania są faktycznie wykonywane w tym samym czasie:

 System.Threading.Tasks.Parallel.For(0, 20000, new ParallelOptions() { MaxDegreeOfParallelism = 5 }, i => 
     { 
      //do something 
     }); 

Powyższy kod wykona 20000 operacji, ale nie wykona więcej niż 5 Operacje u w tym samym czasie.

PODEJMIEMU, dlaczego pracownicy w tle zrobili dla ciebie lepiej, ponieważ stworzyli je i stworzyli instancję na początku, podczas gdy w kodzie próbnym Task wydaje się, że tworzycie nowy obiekt Task dla każdej operacji.

Alternatywnie, czy myślisz o użyciu ustalonej liczby obiektów Task utworzonych na początku, a następnie wykonując podobną akcję z ConcurrentQueue, tak jak w przypadku pracowników w tle? To powinno również okazać się dość skuteczne.