Zawsze miałem wrażenie, że używanie ThreadPool do (powiedzmy niekrytycznych) krótkotrwałych zadań w tle jest uważane za najlepszą praktykę, nawet w ASP.NET, ale potem natknąłem się na ten artykuł, który wydaje się sugerować inaczej - Argumentem jest to, że powinieneś zostawić ThreadPool, aby zająć się żądaniami związanymi z ASP.NET.
Oto jak dotychczas wykonywałem małe asynchroniczne zadania:
ThreadPool.QueueUserWorkItem(s => PostLog(logEvent))
I artykuł sugeruje, zamiast stworzyć wątek wyraźnie podobny do:
new Thread(() => PostLog(logEvent)){ IsBackground = true }.Start()
Pierwsza metoda ma tę zaletę, że jest zarządzana i ograniczona, ale istnieje potencjał (jeśli artykuł jest poprawny), że zadania w tle rywalizują o wątki z programami obsługi żądań ASP.NET. Druga metoda zwalnia ThreadPool, ale kosztem braku ograniczeń, a tym samym potencjalnego wykorzystania zbyt wielu zasobów.
Więc moje pytanie brzmi: czy rada w artykule jest poprawna?
Jeśli Twoja witryna otrzymywała tak duży ruch, że pula wątków się zapełniała, to czy lepiej wyjść poza pasmo, czy też pełna pula wątków sugeruje, że i tak osiągasz limit swoich zasobów. W takim przypadku nie powinieneś próbować rozpoczynać własnych wątków?
Wyjaśnienie: pytam tylko o małe, niekrytyczne zadania asynchroniczne (np. Zdalne rejestrowanie), nie drogie elementy pracy, które wymagałyby oddzielnego procesu (zgadzam się, że w takich przypadkach będziesz potrzebować solidniejszego rozwiązania).
źródło
Odpowiedzi:
Inne odpowiedzi wydają się pomijać najważniejszy punkt:
Jeśli nie próbujesz zrównoleglać operacji intensywnie wykorzystującej procesor, aby wykonać ją szybciej w witrynie o niskim obciążeniu, nie ma sensu w ogóle używać wątku roboczego.
Dotyczy to zarówno wątków wolnych, utworzonych przez
new Thread(...)
, jak i wątków roboczych wThreadPool
odpowiedzi naQueueUserWorkItem
żądania.Tak, to prawda, to może głodować
ThreadPool
w procesie ASP.NET przez kolejkowanie zbyt wielu elementów roboczych. Uniemożliwi to ASP.NET przetwarzanie dalszych żądań. Informacje zawarte w artykule są pod tym względem dokładne; ta sama pula wątkówQueueUserWorkItem
jest również używana do obsługi żądań.Ale jeśli faktycznie umieszczasz w kolejce wystarczającą liczbę elementów roboczych, aby spowodować ten głód, powinieneś zagłodzić pulę wątków! Jeśli wykonujesz dosłownie setki operacji intensywnie wykorzystujących procesor w tym samym czasie, co dobrego przyniesie kolejny wątek roboczy do obsługi żądania ASP.NET, gdy maszyna jest już przeciążona? Jeśli napotkasz taką sytuację, musisz całkowicie przeprojektować!
W większości przypadków widzę lub słyszę, że kod wielowątkowy jest niewłaściwie używany w ASP.NET, ale nie służy do kolejkowania prac intensywnie wykorzystujących procesor. Służy do kolejkowania pracy związanej z we / wy. A jeśli chcesz wykonywać operacje we / wy, powinieneś używać wątku we / wy (port zakończenia we / wy).
W szczególności powinieneś używać wywołań zwrotnych asynchronicznych obsługiwanych przez dowolną klasę biblioteki, której używasz. Metody te są zawsze bardzo wyraźnie oznaczone; zaczynają się od słów
Begin
iEnd
. Jak wStream.BeginRead
,Socket.BeginConnect
,WebRequest.BeginGetResponse
, i tak dalej.Metody te zrobić używać
ThreadPool
, ale używają IOCPs, które mają nie kolidować z żądań ASP.NET. Jest to specjalny rodzaj lekkiego wątku, który może zostać „obudzony” przez sygnał przerwania z systemu I / O. W aplikacji ASP.NET zwykle masz jeden wątek we / wy na każdy wątek roboczy, więc każde żądanie może mieć w kolejce jedną operację asynchroniczną. To dosłownie setki operacji asynchronicznych bez znaczącego pogorszenia wydajności (zakładając, że podsystem we / wy może nadążyć). To o wiele więcej niż kiedykolwiek będziesz potrzebować.Pamiętaj tylko, że delegaci asynchroniczne nie działają w ten sposób - w końcu będą używać wątku roboczego, tak jak
ThreadPool.QueueUserWorkItem
. To tylko wbudowane metody asynchroniczne klas bibliotecznych .NET Framework są w stanie to zrobić. Możesz to zrobić sam, ale jest to skomplikowane i trochę niebezpieczne i prawdopodobnie wykracza poza zakres tej dyskusji.Moim zdaniem najlepszą odpowiedzią na to pytanie jest nieużywanie wystąpienia
ThreadPool
lub w tleThread
w ASP.NET . To wcale nie jest tak, jak uruchamianie wątku w aplikacji Windows Forms, gdzie robisz to, aby interfejs użytkownika był responsywny i nie dbał o jego wydajność. W ASP.NET Twoim problemem jest przepustowość , a całe to przełączanie kontekstu we wszystkich tych wątkach roboczych absolutnie zabije Twoją przepustowość, niezależnie od tego, czy używasz programuThreadPool
.Proszę, jeśli piszesz kod wątkowy w ASP.NET - zastanów się, czy można go przepisać tak, aby używał wcześniej istniejących metod asynchronicznych, a jeśli nie, to zastanów się, czy naprawdę potrzebujesz kodu w ogóle działać w wątku w tle. W większości przypadków prawdopodobnie będziesz dodawać złożoność bez żadnych korzyści netto.
źródło
Action<T>
funkcji wywołania zwrotnego. Jeśli masz na myśli, że wybór, czy użyć wątku roboczego, czy wątku we / wy, ma miejsce na najniższym poziomie, jest to zamierzone; tylko ten poziom może zdecydować, czy potrzebuje IOCP.ThreadPool
ogranicza cię w ten sposób, prawdopodobnie dlatego, że nie ufali programistom, aby zrobili to dobrze. Niezarządzana pula wątków systemu Windows ma bardzo podobny interfejs API, ale w rzeczywistości umożliwia wybór typu wątku.Według Thomasa Marquadta z zespołu ASP.NET w firmie Microsoft można bezpiecznie używać puli wątków ASP.NET (QueueUserWorkItem).
Z artykułu :
źródło
Strony internetowe nie powinny omijać tworzących wątków.
Zwykle przenosisz tę funkcję do usługi systemu Windows, z którą następnie się komunikujesz (używam usługi MSMQ, aby z nimi rozmawiać).
-- Edytować
Implementację opisałem tutaj: Queue-Based Background Processing in ASP.NET MVC Web Application
-- Edytować
Aby wyjaśnić, dlaczego jest to nawet lepsze niż tylko wątki:
Za pomocą usługi MSMQ możesz komunikować się z innym serwerem. Możesz pisać do kolejki między maszynami, więc jeśli z jakiegoś powodu stwierdzisz, że twoje zadanie w tle zbytnio zużywa zasoby głównego serwera, możesz po prostu przenieść to dość trywialnie.
Pozwala także na przetwarzanie wsadowe dowolnego zadania, które próbujesz wykonać (wysyłanie e-maili / cokolwiek).
źródło
Zdecydowanie uważam, że ogólną praktyką w przypadku szybkiej asynchronicznej pracy o niskim priorytecie w ASP.NET byłoby użycie puli wątków .NET, szczególnie w scenariuszach o dużym natężeniu ruchu, ponieważ chcesz, aby zasoby były ograniczone.
Ponadto implementacja wątków jest ukryta - jeśli zaczniesz tworzyć własne wątki, musisz również odpowiednio nimi zarządzać. Nie mówię, że nie możesz tego zrobić, ale po co wymyślać to koło na nowo?
Jeśli wydajność staje się problemem i możesz ustalić, że pula wątków jest czynnikiem ograniczającym (a nie połączenia z bazą danych, wychodzące połączenia sieciowe, pamięć, limity czasu stron itp.), Dostosujesz konfigurację puli wątków, aby zezwolić na więcej wątków roboczych, wyższe żądania w kolejce itp.
Jeśli nie masz problemu z wydajnością, wybranie opcji tworzenia nowych wątków w celu zmniejszenia rywalizacji z kolejką żądań ASP.NET jest klasyczną przedwczesną optymalizacją.
Idealnie byłoby jednak, gdybyś nie musiał używać oddzielnego wątku do wykonywania operacji rejestrowania - po prostu włącz oryginalny wątek, aby ukończył operację tak szybko, jak to możliwe, czyli wtedy, gdy pojawia się MSMQ i oddzielny wątek / proces konsumenta. Zgadzam się, że jest to cięższe i wymaga więcej pracy do wykonania, ale naprawdę potrzebujesz tutaj wytrzymałości - zmienność współdzielonej kolejki w pamięci szybko zużyje jej powitanie.
źródło
Powinieneś używać QueueUserWorkItem i unikać tworzenia nowych wątków, tak jakbyś uniknął zarazy. Aby zobaczyć wizualizację, która wyjaśnia, dlaczego nie będziesz głodować ASP.NET, ponieważ używa on tej samej puli wątków, wyobraź sobie bardzo wykwalifikowanego żonglera używającego dwóch rąk do trzymania pół tuzina kręgli, mieczy lub czegokolwiek w locie. Aby zobaczyć, dlaczego tworzenie własnych wątków jest złe, wyobraź sobie, co dzieje się w Seattle w godzinach szczytu, kiedy mocno używane rampy wjazdowe na autostradę pozwalają pojazdom natychmiast wjechać do ruchu zamiast używać światła i ograniczać liczbę wjazdów do jednego na kilka sekund . Na koniec, aby uzyskać szczegółowe wyjaśnienie, zobacz ten link:
http://blogs.msdn.com/tmarq/archive/2010/04/14/performing-asynchronous-work-or-tasks-in-asp-net-applications.aspx
Dzięki, Thomas
źródło
Ten artykuł nie jest poprawny. ASP.NET ma własną pulę wątków, zarządzanych wątków roboczych, do obsługi żądań ASP.NET. Ta pula zwykle składa się z kilkuset wątków i jest oddzielona od puli ThreadPool, która jest mniejszą wielokrotnością procesorów.
Korzystanie z ThreadPool w ASP.NET nie będzie kolidować z wątkami roboczymi ASP.NET. Korzystanie z ThreadPool jest w porządku.
Dopuszczalne byłoby również skonfigurowanie pojedynczego wątku, który służy tylko do rejestrowania komunikatów i używania wzorca producent / konsument do przekazywania komunikatów dziennika do tego wątku. W takim przypadku, ponieważ wątek jest długotrwały, należy utworzyć pojedynczy nowy wątek, aby uruchomić rejestrowanie.
Używanie nowego wątku do każdej wiadomości to zdecydowanie przesada.
Inną alternatywą, jeśli mówisz tylko o logowaniu, jest użycie biblioteki takiej jak log4net. Obsługuje logowanie w osobnym wątku i zajmuje się wszystkimi problemami z kontekstem, które mogą pojawić się w tym scenariuszu.
źródło
Powiedziałbym, że artykuł jest zły. Jeśli prowadzisz duży sklep .NET, możesz bezpiecznie korzystać z puli w wielu aplikacjach i wielu witrynach internetowych (używając oddzielnych pul aplikacji), po prostu w oparciu o jedno stwierdzenie w dokumentacji puli wątków :
źródło
System.Threading.ThreadPool
.W zeszłym tygodniu zadano mi podobne pytanie w pracy i dam ci taką samą odpowiedź. Dlaczego aplikacje internetowe obsługują wiele wątków na żądanie? Serwer sieciowy to fantastyczny system, mocno zoptymalizowany do dostarczania wielu żądań w odpowiednim czasie (np. Wielowątkowość). Pomyśl, co się dzieje, gdy żądasz prawie każdej strony w sieci.
Podajesz przykład zdalnego logowania, ale to powinno być problemem twojego loggera. Powinien istnieć proces asynchroniczny, aby otrzymywać wiadomości w odpowiednim czasie. Sam wskazuje nawet, że Twój logger (log4net) powinien już to obsługiwać.
Sam ma również rację, że używanie puli wątków w środowisku CLR nie spowoduje problemów z pulą wątków w usługach IIS. Należy jednak zwrócić uwagę na to, że nie tworzysz wątków z procesu, ale tworzysz nowe wątki z wątków puli wątków IIS. Jest różnica i to rozróżnienie jest ważne.
Źródło
źródło
Nie zgadzam się z przywoływanym artykułem (C # feeds.com). Utworzenie nowego wątku jest łatwe, ale niebezpieczne. Optymalna liczba aktywnych wątków do uruchomienia na jednym rdzeniu jest w rzeczywistości zaskakująco niska - poniżej 10. Zbyt łatwo jest spowodować, że maszyna marnuje czas na przełączanie wątków, jeśli wątki są tworzone dla mniejszych zadań. Wątki są zasobami, które WYMAGAJĄ zarządzania. Abstrakcja WorkItem jest do tego celu.
Istnieje tu kompromis między zmniejszeniem liczby wątków dostępnych dla żądań a tworzeniem zbyt wielu wątków, aby którykolwiek z nich mógł wydajnie przetwarzać. Jest to bardzo dynamiczna sytuacja, ale myślę, że powinna być aktywnie zarządzana (w tym przypadku przez pulę wątków), zamiast pozostawiać ją procesorowi, aby wyprzedził tworzenie wątków.
Na koniec artykuł zawiera dość obszerne stwierdzenia dotyczące niebezpieczeństw związanych z używaniem puli wątków, ale naprawdę potrzebuje czegoś konkretnego, aby je poprzeć.
źródło
To, czy usługi IIS używają tej samej puli wątków do obsługi żądań przychodzących, wydaje się trudne do uzyskania ostatecznej odpowiedzi, a także wydaje się, że zmieniły się w różnych wersjach. Dlatego dobrym pomysłem byłoby unikanie nadmiernego używania wątków puli wątków, aby w usługach IIS było ich dużo. Z drugiej strony tworzenie własnego wątku dla każdego małego zadania wydaje się złym pomysłem. Przypuszczalnie masz jakiś rodzaj blokowania w swoim logowaniu, więc tylko jeden wątek mógł postępować naraz, a reszta po prostu na zmianę byłaby planowana i nieplanowana (nie wspominając o narzutach związanych z tworzeniem nowego wątku). Zasadniczo napotykasz dokładnie te problemy, do uniknięcia których została zaprojektowana pula wątków.
Wygląda na to, że rozsądnym kompromisem byłoby przydzielenie aplikacji pojedynczego wątku rejestrowania, do którego można przekazywać wiadomości. Należy uważać, aby wysyłanie wiadomości było tak szybkie, jak to tylko możliwe, aby nie spowalniać aplikacji.
źródło
Możesz użyć Parallel.For lub Parallel.ForEach i zdefiniować limit możliwych wątków, które chcesz przydzielić, aby działały płynnie i zapobiegały zagłodzeniu puli.
Jednak w przypadku uruchamiania w tle konieczne będzie użycie czystego stylu TPL poniżej w aplikacji internetowej ASP.Net.
źródło