Jeśli masz wiele zadań logicznych, które wymagają ciągłego przetwarzania i chcesz, aby były wykonywane równolegle, użyj puli + harmonogramu.
Jeśli chcesz wykonywać zadania związane z we / wy jednocześnie, takie jak pobieranie rzeczy ze zdalnych serwerów lub dostępu do dysku, ale musisz to robić, powiedz co kilka minut, a następnie utwórz własne wątki i zabij je po zakończeniu.
Edycja: Jeśli chodzi o niektóre kwestie, używam pul wątków do uzyskiwania dostępu do bazy danych, fizyki / symulacji, sztucznej inteligencji (gry) i do zadań skryptowych uruchamianych na maszynach wirtualnych, które przetwarzają wiele zadań zdefiniowanych przez użytkownika.
Zwykle pula składa się z 2 wątków na procesor (więc obecnie prawdopodobnie 4), jednak możesz ustawić żądaną liczbę wątków, jeśli wiesz, ile potrzebujesz.
Edycja: Powodem tworzenia własnych wątków są zmiany kontekstu (to znaczy, gdy wątki muszą przełączać się w proces i poza nim, wraz z pamięcią). Posiadanie bezużytecznych zmian kontekstu, powiedzmy, kiedy nie używasz swoich wątków, po prostu pozostawienie ich w spokoju, jak można powiedzieć, może z łatwością zmniejszyć wydajność programu (powiedzmy, że masz 3 uśpione wątki i 2 aktywne wątki). Tak więc, jeśli te pobierające wątki tylko czekają, pochłaniają mnóstwo procesora i ochładzają pamięć podręczną dla twojej prawdziwej aplikacji
Sugerowałbym użycie puli wątków w C # z tych samych powodów, co w przypadku każdego innego języka.
Jeśli chcesz ograniczyć liczbę działających wątków lub nie chcesz, aby narzuty związane z ich tworzeniem i niszczeniem były możliwe, użyj puli wątków.
Przez małe zadania czytana książka oznacza zadania o krótkim czasie życia. Jeśli utworzenie wątku, który działa tylko przez jedną sekundę, zajmuje dziesięć sekund, jest to jedno miejsce, w którym powinieneś używać pul (zignoruj moje rzeczywiste dane, liczy się stosunek).
W przeciwnym razie spędzasz większość czasu na tworzeniu i niszczeniu wątków, zamiast po prostu wykonywać pracę, do której są przeznaczone.
źródło
Oto ładne podsumowanie puli wątków w .Net: http://blogs.msdn.com/pedram/archive/2007/08/05/dedicated-thread-or-a-threadpool-thread.aspx
Post zawiera również kilka wskazówek, kiedy nie powinieneś używać puli wątków i zamiast tego rozpoczynać własny wątek.
źródło
Gorąco polecam przeczytanie tego bezpłatnego e-booka: Threading in C # autorstwa Josepha Albahariego
Przeczytaj przynajmniej sekcję „Pierwsze kroki”. E-book stanowi świetne wprowadzenie i zawiera również wiele zaawansowanych informacji o wątkach.
Wiedza o tym, czy używać puli wątków, to dopiero początek. Następnie musisz określić, która metoda wprowadzania puli wątków najlepiej odpowiada Twoim potrzebom:
Ten e-book wyjaśnia to wszystko i radzi, kiedy ich używać, a kiedy tworzyć własny wątek.
źródło
Pula wątków została zaprojektowana w celu zmniejszenia przełączania kontekstów między wątkami. Rozważmy proces, w którym działa kilka komponentów. Każdy z tych komponentów może tworzyć wątki robocze. Im więcej wątków w procesie, tym więcej czasu marnuje się na przełączanie kontekstów.
Teraz, gdyby każdy z tych składników kolejkował elementy do puli wątków, miałbyś znacznie mniej narzutu przełączania kontekstu.
Pula wątków została zaprojektowana w celu maksymalizacji pracy wykonywanej na Twoich procesorach (lub rdzeniach procesora). Dlatego domyślnie pula wątków obraca wiele wątków na procesor.
Istnieją sytuacje, w których nie chcesz używać puli wątków. Jeśli czekasz na we / wy, czekasz na zdarzenie itp., Wiążesz ten wątek puli wątków i nie może być używany przez nikogo innego. Ta sama idea odnosi się do długotrwałych zadań, chociaż to, co stanowi długotrwałe zadanie, jest subiektywne.
Pax Diablo również ma rację. Rozwijanie wątków nie jest darmowe. Zajmuje to trochę czasu i zajmuje dodatkowe miejsce w stosie. Pula wątków ponownie wykorzysta wątki, aby zamortyzować ten koszt.
Uwaga: zapytałeś o użycie wątku puli wątków do pobierania danych lub wykonywania operacji we / wy dysku. Nie powinieneś używać do tego wątku puli wątków (z powodów, które opisałem powyżej). Zamiast tego użyj asynchronicznych operacji we / wy (znanych również jako metody BeginXX i EndXX). Na to
FileStream
byłobyBeginRead
iEndRead
. Na toHttpWebRequest
byłobyBeginGetResponse
iEndGetResponse
. Są bardziej skomplikowane w użyciu, ale są właściwym sposobem wykonywania wielowątkowych operacji we / wy.źródło
Uważaj na pulę wątków .NET w przypadku operacji, które mogą blokować jakąkolwiek istotną, zmienną lub nieznaną część ich przetwarzania, ponieważ jest ona podatna na zanik wątków. Rozważ użycie równoległych rozszerzeń .NET, które zapewniają dużą liczbę logicznych abstrakcji w operacjach wątkowych. Obejmują one również nowy harmonogram, który powinien być ulepszeniem puli wątków. Zobacz tutaj
źródło
Jednym z powodów używania puli wątków tylko do małych zadań jest ograniczona liczba wątków w puli wątków. Jeśli jeden jest używany przez długi czas, uniemożliwia użycie tego wątku przez inny kod. Jeśli zdarzy się to wiele razy, pula wątków może zostać wykorzystana.
Korzystanie z puli wątków może mieć subtelne efekty - na przykład niektóre liczniki czasu .NET używają wątków puli wątków i nie będą uruchamiane.
źródło
Jeśli masz zadanie w tle, które będzie działać przez długi czas, na przykład przez cały okres istnienia Twojej aplikacji, stworzenie własnego wątku jest rozsądną rzeczą. Jeśli masz krótkie zadania, które należy wykonać w wątku, użyj puli wątków.
W aplikacji, w której tworzysz wiele wątków, narzut związany z tworzeniem wątków staje się znaczny. Korzystanie z puli wątków tworzy wątki raz i używa ich ponownie, unikając w ten sposób narzutu tworzenia wątków.
W aplikacji, nad którą pracowałem, przejście od tworzenia wątków do korzystania z puli wątków dla wątków krótkotrwałych naprawdę pomogło w jej wdrożeniu.
źródło
Aby uzyskać najwyższą wydajność z współbieżnie wykonywanymi jednostkami, napisz własną pulę wątków, w której pula obiektów Thread jest tworzona podczas uruchamiania i przejdź do blokowania (wcześniej zawieszonego), czekając na kontekst do uruchomienia (obiekt ze standardowym interfejsem zaimplementowanym przez Twój kod).
Tak wiele artykułów na temat zadań, wątków i puli wątków platformy .NET nie zawiera informacji niezbędnych do podjęcia decyzji o wydajności. Ale kiedy je porównasz, wygrywają wątki, a zwłaszcza pula wątków. Są najlepiej dystrybuowane między procesorami i uruchamiają się szybciej.
Należy omówić fakt, że główna jednostka wykonawcza systemu Windows (w tym Windows 10) jest wątkiem, a narzut przełączania kontekstu systemu operacyjnego jest zwykle pomijalny. Mówiąc najprościej, nie byłem w stanie znaleźć przekonujących dowodów na wiele z tych artykułów, niezależnie od tego, czy w artykule podano wyższą wydajność poprzez oszczędność przełączania kontekstu, czy lepsze wykorzystanie procesora.
Teraz trochę realizmu:
Większość z nas nie potrzebuje, aby nasza aplikacja była deterministyczna, a większość z nas nie ma tła z wątkami, co często wiąże się na przykład z tworzeniem systemu operacyjnego. To, co napisałem powyżej, nie jest dla początkującego.
Dlatego najważniejsze może być omówienie tego, co jest łatwe do zaprogramowania.
Jeśli utworzysz własną pulę wątków, będziesz mieć trochę do zrobienia, ponieważ będziesz musiał zająć się śledzeniem stanu wykonania, jak symulować zawieszenie i wznowienie oraz jak anulować wykonanie - w tym w całej aplikacji zamknąć. Być może będziesz musiał się również zastanowić, czy chcesz dynamicznie rozwijać swoją pulę, a także jakie ograniczenia pojemności będą miały Twoja pula. Mogę napisać taki framework w godzinę, ale to dlatego, że robiłem to wiele razy.
Być może najłatwiejszym sposobem napisania jednostki wykonawczej jest użycie zadania. Piękno zadania polega na tym, że możesz go utworzyć i uruchomić w kodzie (chociaż może być wymagana ostrożność). Możesz przekazać token anulowania do obsługi, gdy chcesz anulować zadanie. Ponadto stosuje podejście obiecujące do łączenia zdarzeń w łańcuchy i może zwrócić określony typ wartości. Co więcej, dzięki async i await istnieje więcej opcji, a Twój kod będzie bardziej przenośny.
W istocie ważne jest, aby zrozumieć zalety i wady funkcji Tasks, Threads i .NET ThreadPool. Jeśli potrzebuję wysokiej wydajności, zamierzam używać wątków i wolę używać własnej puli.
Łatwym sposobem porównania jest uruchomienie 512 wątków, 512 zadań i 512 wątków puli wątków. Na początku znajdziesz opóźnienie w Threads (stąd po co pisać pulę wątków), ale wszystkie 512 wątków będzie działać w ciągu kilku sekund, podczas gdy zadania i wątki .NET ThreadPool zajmują do kilku minut, aby rozpocząć.
Poniżej wyniki takiego testu (czterordzeniowy i5 z 16 GB RAM-u), dając każdemu 30 sekund na uruchomienie. Wykonywany kod wykonuje proste operacje we / wy pliku na dysku SSD.
Wyniki testów
źródło
Pule wątków są świetne, gdy masz więcej zadań do przetworzenia niż dostępnych wątków.
Możesz dodać wszystkie zadania do puli wątków i określić maksymalną liczbę wątków, które mogą być uruchomione w określonym czasie.
Sprawdź tę stronę w witrynie MSDN: http://msdn.microsoft.com/en-us/library/3dasc8as(VS.80).aspx
źródło
Jeśli możesz, zawsze używaj puli wątków, pracuj na najwyższym możliwym poziomie abstrakcji. Pule wątków ukrywają tworzenie i niszczenie wątków, zwykle jest to dobra rzecz!
źródło
W większości przypadków możesz korzystać z puli, ponieważ unikasz kosztownego procesu tworzenia wątku.
Jednak w niektórych scenariuszach możesz chcieć utworzyć wątek. Na przykład, jeśli nie jesteś jedyną osobą korzystającą z puli wątków, a utworzony wątek jest długotrwały (aby uniknąć zużywania zasobów udostępnionych) lub na przykład jeśli chcesz kontrolować rozmiar stosu wątku.
źródło
Nie zapomnij zbadać pracownika w tle.
Znajduję w wielu sytuacjach, daje mi to, czego chcę, bez podnoszenia ciężarów.
Twoje zdrowie.
źródło
Zwykle używam Threadpool, gdy muszę po prostu zrobić coś w innym wątku i nie obchodzi mnie, kiedy działa lub kończy. Coś jak rejestrowanie, a może nawet pobieranie pliku w tle (chociaż są lepsze sposoby na zrobienie tego w stylu asynchronicznym). Używam własnego wątku, gdy potrzebuję większej kontroli. Odkryłem również, że używanie kolejki Threadsafe (zhakuj własną) do przechowywania "obiektów poleceń" jest przyjemne, gdy mam wiele poleceń, nad którymi muszę pracować w> 1 wątku. Możesz więc podzielić plik Xml i umieścić każdy element w kolejce, a następnie mieć wiele wątków pracujących nad przetwarzaniem tych elementów. Napisałem taką kolejkę już na uni (VB.net!), Że przekonwertowałem na C #. Zawarłem to poniżej bez konkretnego powodu (ten kod może zawierać błędy).
using System.Collections.Generic; using System.Threading; namespace ThreadSafeQueue { public class ThreadSafeQueue<T> { private Queue<T> _queue; public ThreadSafeQueue() { _queue = new Queue<T>(); } public void EnqueueSafe(T item) { lock ( this ) { _queue.Enqueue(item); if ( _queue.Count >= 1 ) Monitor.Pulse(this); } } public T DequeueSafe() { lock ( this ) { while ( _queue.Count <= 0 ) Monitor.Wait(this); return this.DeEnqueueUnblock(); } } private T DeEnqueueUnblock() { return _queue.Dequeue(); } } }
źródło
Chciałem, aby pula wątków rozdzielała pracę między rdzeniami z jak najmniejszym opóźnieniem, a to nie musiało dobrze współgrać z innymi aplikacjami. Okazało się, że wydajność puli wątków .NET nie była tak dobra, jak mogłaby być. Wiedziałem, że chcę mieć jeden wątek na rdzeń, więc napisałem własną klasę zastępczą puli wątków. Kod jest dostarczany jako odpowiedź na inne pytanie StackOverflow tutaj .
Jeśli chodzi o pierwotne pytanie, pula wątków jest przydatna do dzielenia powtarzalnych obliczeń na części, które mogą być wykonywane równolegle (zakładając, że mogą być wykonywane równolegle bez zmiany wyniku). Ręczne zarządzanie wątkami jest przydatne w przypadku zadań takich jak interfejs użytkownika i we / wy.
źródło