2012-07-15 6 views
5
  • Mam operację intensywnej operacji we/wy.
  • Chcę tylko MAX z 5 wątków kiedykolwiek działa w tym samym czasie.
  • Mam 8000 zadań do kolejkowania i zakończenia.
  • Każde wykonanie zadania trwa około 15-20 sekund.

Mam rozejrzał się puli wątków, aleFrustracje wątku - tworzenie wątku przekraczającego wartość SetMaxThreads

 ThreadPool.SetMaxThreads(5, 0); 

     List<task> tasks = GetTasks(); 

     int toProcess = tasks.Count; 
     ManualResetEvent resetEvent = new ManualResetEvent(false); 

     for (int i = 0; i < tasks.Count; i++) 
     { 
      ReportGenerator worker = new ReportGenerator(tasks[i].Code, id); 
      ThreadPool.QueueUserWorkItem(x => 
      { 
       worker.Go(); 
       if (Interlocked.Decrement(ref toProcess) == 0) 
        resetEvent.Set(); 
      }); 
     } 

     resetEvent.WaitOne(); 

Nie mogę zrozumieć, dlaczego ... mój kod jest wykonywany więcej niż 5 wątków jednocześnie. Próbowałem setmaxthreads, setmuthreads, ale nadal wykonuje więcej niż 5 wątków.

Co się dzieje? czego mi brakuje? Czy powinienem to robić w inny sposób?

Dzięki

+0

Zweryfikowałeś wartość ** tasks.Count ** w debugerze? Czy próbowałeś zamiast tego wstawić "5"? –

+0

Tablica zadań zawiera ~ 8000 obiektów. – Mike

Odpowiedz

3

Zadanie Biblioteka Parallel może pomóc:

List<task> tasks = GetTasks(); 

Parallel.ForEach(tasks, new ParallelOptions { MaxDegreeOfParallelism = 5 }, 
    task => {ReportGenerator worker = new ReportGenerator(task.Code, id); 
      worker.Go();}); 

What does MaxDegreeOfParallelism do?

+0

To takie proste! I działał jak urok! Dziękuję Ci! – Mike

1

Myślę, że jest inny i lepszy sposób podejścia do tego. (Przepraszam, jeśli przypadkowo Java-ize część składni)

Główny wątek tutaj ma listę rzeczy do zrobienia w "Zadaniach" - zamiast tworzyć wątki dla każdego zadania, co jest naprawdę nieefektywne, kiedy mieć tak wiele elementów, utworzyć żądaną liczbę wątków, a następnie poprosić ich o zadanie zadań z listy w razie potrzeby.

Pierwszą rzeczą do zrobienia jest dodanie zmiennej do klasy, z której pochodzi ten kod, do użycia jako wskaźnik na liście. Dodamy także jeden dla maksymalnej liczby oczekujących.

// New variable in your class definition 
private int taskStackPointer; 
private final static int MAX_THREADS = 5; 

Utwórz metodę, która zwraca kolejne zadanie na liście i zwiększa wskaźnik stosu. Następnie należy utworzyć nowy interfejs dla tego:

// Make sure that only one thread has access at a time 
[MethodImpl(MethodImplOptions.Synchronized)] 
public task getNextTask() 
{ 
    if(taskStackPointer < tasks.Count) 
     return tasks[taskStackPointer++]; 
    else 
     return null; 
} 

Naprzemiennie można powrócić zadania [taskStackPointer ++] kod, jeśli istnieje wartość można określić w ten sposób, „koniec listy”.. Prawdopodobnie jednak łatwiej to zrobić w ten sposób.

Interfejs:

public interface TaskDispatcher 
{ 
    [MethodImpl(MethodImplOptions.Synchronized)] public task getNextTask(); 
} 

W klasie ReportGenerator zmień konstruktora zaakceptować dyspozytora obiektu:

public ReportGenerator(TaskDispatcher td, int idCode) 
{ 
    ... 
} 

Trzeba także zmienić ReportGenerator klasę tak, że przetwarzanie ma zewnętrzną pętlę, która rozpoczyna się od wywołania td.getNextTask(), aby zażądać nowego zadania, i które kończy pętlę, gdy odzyskuje NULL.

Wreszcie zmieniać kod tworzenia wątku do czegoś takiego: (jest to po prostu daje wyobrażenie)

taskStackPointer = 0; 
for (int i = 0; i < MAX_THREADS; i++) 
{ 
    ReportGenerator worker = new ReportGenerator(this,id); 
    worker.Go(); 
} 

ten sposób można stworzyć żądaną liczbę wątków i zachować je wszystkie pracy w charakterze max .

(Nie jestem pewien, czy mam użycie "[MethodImpl (MethodImplOptions.Synchronized)]" dokładnie tak ...Jestem bardziej przyzwyczajony do Java niż C#)

+0

Dzięki za poświęcenie czasu na odpowiedź na moje pytanie, ma to sens. Ta metoda jest jednak bardziej gadatliwa: P – Mike

+0

Może to być trochę bardziej szczegółowe, ale gdy już to zrobisz, jest to bardzo wydajne i łatwe do zrozumienia. –

1

Twoja lista zadań będzie miał 8k elementy w nim, ponieważ powiedziano kod, aby umieścić je tam :

List<task> tasks = GetTasks(); 

powiedział, że liczba ta nie ma nic wspólnego z tym, jak wiele wątków są wykorzystywane w tym sensie, że debugger jest zawsze pokaże ile przedmiotów reklamę dedykuję do listy.

Istnieją różne sposoby ustalania liczby używanych wątków. Być może jednym z najprostszych jest włamanie się do aplikacji za pomocą debuggera i przejrzenie okna wątków. Nie tylko uzyskasz wynik, ale zobaczysz, co robi każdy wątek (lub nie), co prowadzi mnie do ...

Istnieje wiele dyskusji na temat twoich zadań i sposobu dotarcia przy liczbie do "dławienia" puli wątków. W większości przypadków pula wątków zrobi dobrze.

teraz, aby odpowiedzieć na konkretne pytanie ...

Aby jawnie kontrolować liczbę jednoczesnych zadań, za realizację trywialne, który objąłby zmieniając swoją kolekcję zadanie z listy, aby BlockingCollection (która wewnętrznie użyć ConcurrentQueue) i następujący kod do „konsumować” pracę:

var parallelOptions = new ParallelOptions 
{ 
    MaxDegreeOfParallelism = 5 
}; 

Parallel.ForEach(collection.GetConsumingEnumerable(), options, x => 
{ 
    // Do work here... 
}); 

Zmień MaxDegreeOfParallelism aby cokolwiek wartość współbieżne ustaleniu jest odpowiedni do pracy robisz.

Poniższy może być interesujące dla Ciebie:

Parallel.ForEach Method

BlockingCollection

Chris

3

Jest to ograniczenie w SetMaxThreads tym, że nigdy nie można ustawić go niższa niż liczba procesory w systemie. Jeśli masz 8 procesorów, ustawienie na 5 jest takie samo, jak brak wywoływania funkcji.