2008-09-01 11 views
8

Próbuję wymyślić projekt puli wątków z wieloma wymaganiami projektowymi dla mojej pracy. Jest to prawdziwy problem dla działającego oprogramowania i jest to trudne zadanie. Mam działającą implementację, ale chciałbym rzucić to na SO i zobaczyć, jakie ciekawe pomysły mogą wymyślić ludzie, aby móc porównać z moją implementacją i zobaczyć, jak się układa. Próbowałem być tak dokładny, jak to tylko możliwe.Pula wątków do wykonywania arbitralnych zadań o różnych priorytetach

Pula wątków musi wykonać serię zadań. Zadania mogą być krótkie (< 1sec) lub długotrwałe (godziny lub dni). Każde zadanie ma przypisany priorytet (od 1 = bardzo niski do 5 = bardzo wysoki). Zadania mogą przychodzić w dowolnym momencie, podczas gdy inne zadania są uruchomione, więc po ich otrzymaniu pula wątków musi je wybrać i zaplanować je, gdy wątki staną się dostępne.

Priorytet zadania jest całkowicie niezależny od długości zadania. W rzeczywistości niemożliwe jest określenie, ile czasu może upłynąć zadanie, aby uruchomić go bez uruchamiania.

Niektóre zadania są związane z procesorem, a niektóre z nich są bardzo powiązane z IO. Nie da się wcześniej przewidzieć, jakie będzie dane zadanie (choć wydaje mi się, że możliwe jest wykrycie podczas wykonywania zadań).

Podstawowym celem puli wątków jest maksymalizacja przepustowości. Pula wątków powinna efektywnie wykorzystywać zasoby komputera. Idealnie, dla zadań związanych z CPU, liczba aktywnych wątków będzie równa liczbie procesorów. W przypadku zadań związanych z IO należy przypisać więcej wątków niż w przypadku procesorów, aby blokowanie nie miało nadmiernego wpływu na przepustowość. Ważne jest minimalizowanie użycia zamków i używanie bezpiecznych/szybkich pojemników z gwintem.

Generalnie powinieneś wykonywać zadania o wyższym priorytecie z wyższym priorytetem procesora (patrz: SetThreadPriority). Zadania o niższym priorytecie nie powinny "blokować" zadań o wyższym priorytecie przed uruchomieniem, więc jeśli zadanie o wyższym priorytecie pojawi się podczas wykonywania wszystkich zadań o niskim priorytecie, zadanie o wyższym priorytecie zostanie uruchomione.

Do zadań przypisany jest parametr "max running tasks". Każdemu typowi zadania można uruchomić najwyżej tyle jednoczesnych instancji zadania naraz. Na przykład, mamy następujące zadania w kolejce:

  • A - 1000 przypadków - niskim priorytecie - max zgłoszenia 1
  • B - 1000 przypadków - niskim priorytecie - max zadania 1
  • C - 1000 instancje - niski priorytet - max zadania 1

Robocza realizacja mogła przebiegać (maksymalnie) 1 A, 1 B i 1 C w tym samym czasie.

Musi działać w systemie Windows XP, Server 2003, Vista i Server 2008 (najnowsze dodatki Service Pack).


Dla porównania, możemy kliknąć na poniższy interfejs:

namespace ThreadPool 
{ 
    class Task 
    { 
    public: 
     Task();  
     void run(); 
    }; 

    class ThreadPool 
    {  
    public: 
     ThreadPool(); 
     ~ThreadPool(); 

     void run(Task *inst); 
     void stop(); 
    }; 
} 

Odpowiedz

5

Co więc zamierzamy wybrać jako podstawowy element do tego celu. Windows ma dwa bloki, które wyglądają obiecująco: - I/O Completion Ports (IOCP) i Asynchronous Procedure Calls (APC). Oba te elementy dają nam kolejkowanie FIFO bez konieczności wykonywania jawnego blokowania i z pewną ilością wbudowanego wsparcia systemu operacyjnego w takich miejscach, jak harmonogram (na przykład, IOCP mogą unikać niektórych przełączeń kontekstowych).

APC to być może nieco lepsze dopasowanie, ale będziemy musieli być ostrożni, ponieważ nie są one "przezroczyste". Jeśli element roboczy wykonuje wyczekiwane oczekiwanie (:: SleepEx, :: WaitForXxxObjectEx, itp.) I przypadkowo wysyłamy APC do wątku, to nowo wysłane APC przejmie wątek, zawieszając poprzednio wykonujące APC, aż nowy APC skończone. Jest to szkodliwe dla naszych wymagań dotyczących współbieżności i może sprawić, że przepełnienie stosu stanie się bardziej prawdopodobne.

1

To musi uruchomić na Windows XP, Server 2003, Vista i Server 2008 (najnowsze dodatki Service Pack).

Jaką cechę wbudowanych pul wątków systemu sprawia, że ​​nie nadają się one do tego zadania?Jeśli chcesz kierować reklamy na XP i 2003, nie możesz korzystać z nowych, błyszczących puli Vista/2008, ale nadal możesz używać QueueUserWorkItem i znajomych.

0

@DrPizza - to bardzo dobre pytanie, które trafia prosto w sedno problemu. Istnieje kilka powodów, dla których QueueUserWorkItem i pula wątków systemu Windows NT została wykluczona (chociaż Vista wygląda ciekawie, może za kilka lat).

Po pierwsze, chcieliśmy mieć większą kontrolę nad uruchamianiem i zatrzymywaniem wątków. Słyszeliśmy, że pulę wątków NT niechętnie uruchamia nowy wątek, jeśli uważa, że ​​zadania są krótkotrwałe. Moglibyśmy użyć WT_EXECUTELONGFUNCTION, ale naprawdę nie mamy pojęcia, czy zadanie jest długie czy krótkie

Po drugie, jeśli pula wątków była już wypełniona długotrwałymi zadaniami o niskim priorytecie, nie byłoby szans na wysoki priorytet zadanie zaczyna działać w odpowiednim czasie. Puli wątków NT nie ma prawdziwej koncepcji priorytetów zadań, więc nie możemy zrobić QueueUserWorkItem i powiedzieć "o tak przy okazji, uruchom to natychmiast".

Po trzecie (zgodnie z MSDN) pula wątków NT nie jest zgodna z modelem mieszkania STA. Nie jestem pewien, co by to miało znaczyć, ale wszystkie nasze wątki robocze działają w STA.

0

@DrPizza - to bardzo dobre pytanie, które trafia prosto w sedno problemu. Istnieje kilka powodów, dla których QueueUserWorkItem i pula wątków systemu Windows NT została wykluczona (chociaż Vista wygląda ciekawie, może za kilka lat).

Tak, wygląda na to, że w Visty jest dość urozmaicony.

OK, nadal nie mam pewności, jak chcesz, aby priorytety działały. Jeśli w puli aktualnie działa zadanie typu A z maksymalną współbieżnością równą 1 i niskim priorytetem, a otrzyma ono nowe zadanie również o typie A (i maksymalnej współbieżności 1), ale tym razem o wysokim priorytecie, co powinien zrobić? ?

Zawieszenie aktualnie wykonywanego A jest włochate (może posiadać blokadę, którą musi wykonać nowe zadanie, zakleszczając system). Nie może on odrodzić drugiego wątku i po prostu pozwolić mu działać równolegle (dozwolona współbieżność wynosi tylko 1). Ale nie może czekać, aż zadanie o niskim priorytecie zostanie ukończone, ponieważ środowisko wykonawcze nie jest ograniczone, co może pozwolić zadaniu o niskim priorytecie zablokować zadanie o wysokim priorytecie.

Moim domniemaniem jest, że jest to drugie zachowanie, o które prosisz?

0

@DrPizza:

OK, jestem jeszcze trochę niejasne, jak chcesz priorytety do pracy. Jeśli basen jest aktualnie uruchomione zadanie typu A z maksymalną współbieżności z 1 i niskim priorytecie i pobiera dane nowe zadanie również typu A (i maksymalnej współbieżności 1), lecz tym razem z wysoki priorytet, co powinien zrobić?

Ten jest trochę skomplikowany, chociaż w tym przypadku, byłbym zadowolony z tego, że zadanie o niskim priorytecie będzie działać do końca. Zwykle nie widzimy wielu takich samych typów zadań z różnymi priorytetami wątków. W naszym modelu faktycznie można bezpiecznie zatrzymać, a następnie ponownie uruchomić zadania w pewnych ściśle określonych punktach (z różnych powodów), chociaż komplikacje, które wprowadzą prawdopodobnie nie są warte ryzyka.

Zwykle tylko różne typy zadań mają różne priorytety. Na przykład:

  • zadanie - 1000 przypadki - niski priorytet
  • B zadanie - 1000 przypadki - wysoki priorytet

Zakładając zadania A miał przyjść i biegali, a następnie zadania B miał przybył, chcielibyśmy, aby zadania B były w stanie przebiegać mniej więcej od razu.

Powiązane problemy