Korzystanie z ThreadPool.QueueUserWorkItem w ASP.NET w scenariuszu dużego ruchu

111

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).

Michael Hart
źródło
Fabuła gęstnieje - znalazłem ten artykuł ( blogs.msdn.com/nicd/archive/2007/04/16/… ), którego nie do końca mogę rozszyfrować. Z jednej strony wydaje się mówić, że IIS 6.0+ zawsze przetwarza żądania dotyczące wątków roboczych puli wątków (i że starsze wersje mogą to robić), ale jest to: „Jeśli jednak używasz nowych asynchronicznych stron .NET 2.0 ( Async = "true") lub ThreadPool.QueueUserWorkItem (), wtedy asynchroniczna część przetwarzania zostanie wykonana wewnątrz [wątku portu zakończenia]. " Asynchroniczna część przetwarzania ?
Jeff Sternal
Jeszcze jedno - powinno to być łatwe do przetestowania w instalacji usług IIS 6.0+ (której nie mam w tej chwili), sprawdzając, czy dostępne wątki robocze puli wątków są niższe niż maksymalne wątki robocze, a następnie robiąc to samo w kolejce przedmioty do pracy.
Jeff Sternal

Odpowiedzi:

104

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 w ThreadPoolodpowiedzi na QueueUserWorkItemżądania.

Tak, to prawda, to może głodować ThreadPoolw 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ów QueueUserWorkItemjest 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 Begini End. Jak w Stream.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 tle Threadw 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 programu ThreadPool.

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.

Aaronaught
źródło
Dzięki za tę szczegółową odpowiedź i masz rację, staram się używać metod asynchronicznych, gdy to możliwe (w połączeniu z kontrolerami asynchronicznymi w ASP.NET MVC). W przypadku mojego przykładu ze zdalnym rejestratorem właśnie to mogę zrobić. Jest to jednak interesujący problem projektowy, ponieważ przenosi obsługę asynchroniczną aż do najniższego poziomu kodu (tj. Implementacji rejestratora), zamiast być w stanie zdecydować o tym, powiedzmy, z poziomu kontrolera (w tym drugim przypadku , potrzebowałbyś na przykład dwóch implementacji rejestratora, aby móc wybierać).
Michael Hart
@Michael: Asynchroniczne wywołania zwrotne są generalnie dość łatwe do zapakowania, jeśli chcesz podnieść je o więcej poziomów; można utworzyć fasadę wokół metod asynchronicznych i opakować je na przykład jedną metodą, która używa 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.
Aaronaught
Chociaż, jako interesujący punkt, tylko .NET ThreadPoologranicza 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.
Aaronaught
2
Porty I / O Completion (IOCP). opis IOCP nie jest całkiem poprawny. w IOCP masz stałą liczbę wątków roboczych, które na zmianę pracują nad WSZYSTKIMI oczekującymi zadaniami. nie należy ich mylić z pulami wątków, które mogą być stałe lub dynamiczne, ALE mają jeden wątek na zadanie - strasznie się skalują. w przeciwieństwie do ASYNC, nie masz jednego wątku na zadanie. Wątek IOCP może trochę działać na zadaniu 1, następnie przełącz się na zadanie 3, zadanie 2, a następnie z powrotem do zadania 1. stany sesji zadań są zapisywane i przekazywane między wątkami.
MickyD
1
A co z wstawkami do bazy danych? Czy istnieje polecenie ASYNC SQL (np. Wykonanie)? Wstawianie bazy danych to najwolniejsza operacja we / wy (z powodu blokowania), a oczekiwanie przez główny wątek na wstawienie wierszy jest po prostu stratą cykli procesora.
Ian Thompson
45

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 :

P) Jeśli moja aplikacja ASP.NET używa wątków CLR ThreadPool, czy nie będę głodował ASP.NET, który również używa CLR ThreadPool do wykonywania żądań? ..

A) Podsumowując, nie martw się o głodzenie ASP.NET wątków, a jeśli uważasz, że jest tu problem, daj mi znać, a my się nim zajmiemy.

P) Czy powinienem tworzyć własne wątki (nowy wątek)? Czy to nie będzie lepsze dla ASP.NET, ponieważ używa on puli wątków CLR.

A) Proszę nie. Albo inaczej, nie !!! Jeśli jesteś naprawdę sprytny - znacznie mądrzejszy ode mnie - możesz tworzyć własne wątki; w przeciwnym razie nawet o tym nie myśl. Oto kilka powodów, dla których nie powinieneś często tworzyć nowych wątków:

  1. Jest bardzo drogi w porównaniu do QueueUserWorkItem ... Swoją drogą, jeśli potrafisz napisać lepszą ThreadPool niż CLR, zachęcam do aplikowania o pracę w Microsoft, bo zdecydowanie szukamy ludzi takich jak Ty !.
Tim P.
źródło
4

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).

Noon Silk
źródło
4
Nie zgodziłbym się, że to ogólne stwierdzenie jest zawsze prawdziwe - szczególnie w przypadku zadań niekrytycznych. Tworzenie usługi Windows tylko do celów asynchronicznego rejestrowania zdecydowanie wydaje się przesadą. Poza tym ta opcja nie zawsze jest dostępna (możliwość wdrożenia usługi MSMQ i / lub usługi Windows).
Michael Hart
Jasne, ale jest to „standardowy” sposób implementacji zadań asynchronicznych ze strony internetowej (motyw kolejki w stosunku do innego procesu).
Noon Silk
2
Nie wszystkie zadania asynchroniczne są sobie równe, dlatego na przykład strony asynchroniczne istnieją w ASP.NET. Jeśli chcę pobrać wynik ze zdalnej usługi internetowej do wyświetlenia, nie zamierzam tego robić za pośrednictwem usługi MSMQ. W tym przypadku piszę do dziennika za pomocą zdalnego posta. Pisanie usługi systemu Windows, ani podłączanie do tego usługi MSMQ nie pasuje do problemu (ani nie mogę, ponieważ ta konkretna aplikacja jest na platformie Azure).
Michael Hart
1
Zastanów się: piszesz do zdalnego hosta? Co się stanie, jeśli ten host jest wyłączony lub nieosiągalny? Czy zechcesz ponownie spróbować pisać? Może tak, może nie. W przypadku Twojej implementacji trudno jest ponowić próbę. Dzięki usłudze staje się to dość trywialne. Doceniam, że możesz nie być w stanie tego zrobić i pozwolę komuś innemu odpowiedzieć na konkretne problemy z tworzeniem wątków ze stron internetowych [tj. Jeśli Twój wątek nie był w tle itp.], Ale nakreślę „właściwą” sposób na zrobienie tego. Nie znam lazuru, chociaż użyłem ec2 (można na nim zainstalować system operacyjny, więc wszystko jest w porządku).
Noon Silk
@silky, dzięki za komentarze. Powiedziałem „niekrytyczne”, aby uniknąć tego cięższego (ale wytrzymałego) rozwiązania. Wyjaśniłem to pytanie, aby było jasne, że nie proszę o sprawdzone metody dotyczące elementów roboczych w kolejce. Platforma Azure obsługuje ten typ scenariusza (ma własny magazyn kolejki) - ale operacja kolejkowania jest zbyt kosztowna w przypadku rejestrowania synchronicznego, więc i tak potrzebowałbym rozwiązania asynchronicznego. W moim przypadku zdaję sobie sprawę z pułapek awarii, ale nie zamierzam dodawać więcej infrastruktury na wypadek, gdyby ten konkretny dostawca rejestrowania zawiódł - mam też innych dostawców rejestrowania.
Michael Hart
4

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.

Sam
źródło
2

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

Tomasz
źródło
Ten link jest bardzo przydatny, dzięki za to Thomas. Chciałbym również usłyszeć, co myślisz o odpowiedzi @ Aaronaught.
Michael Hart
Zgadzam się z Aaronaught i powiedziałem to samo na moim blogu. Ujmę to w ten sposób: „Próbując uprościć tę decyzję, przełącz się [do innego wątku] tylko wtedy, gdy w przeciwnym razie zablokujesz wątek żądania ASP.NET, podczas gdy nic nie robisz. Jest to nadmierne uproszczenie, ale próbuję aby decyzja była prosta ”. Innymi słowy, nie rób tego dla nieblokującej pracy obliczeniowej, ale rób to, jeśli wysyłasz asynchroniczne żądania usługi sieci Web do zdalnego serwera. Posłuchaj Aaronaughta! :)
Thomas
1

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.

Samuel Neff
źródło
1
@Sam, właściwie używam log4net i nie widzę logów zapisywanych w osobnym wątku - czy jest jakaś opcja, którą muszę włączyć?
Michael Hart
1

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 :

Na proces przypada jedna pula wątków. Pula wątków ma domyślny rozmiar 250 wątków roboczych na dostępny procesor i 1000 wątków zakończenia operacji we / wy. Liczbę wątków w puli wątków można zmienić przy użyciu metody SetMaxThreads. Każdy wątek używa domyślnego rozmiaru stosu i działa z domyślnym priorytetem.

Christopher Nobles
źródło
Jedna aplikacja działająca w jednym procesie jest w pełni zdolna do samoczynnego wyłączenia! (Lub przynajmniej pogorszenie własnej wydajności na tyle, aby pula wątków była propozycją przegraną.)
Jeff Sternal,
Więc zgaduję, że żądania ASP.NET używają wątków zakończenia we / wy (w przeciwieństwie do wątków roboczych) - czy to prawda?
Michael Hart
Z artykułu Fritza Oniona podlinkowałem w mojej odpowiedzi: „Ten paradygmat zmienia [z IIS 5.0 na IIS 6.0] sposób obsługi żądań w ASP.NET. Zamiast wysyłać żądania z inetinfo.exe do procesu roboczego ASP.NET, http. sys bezpośrednio kolejkuje każde żądanie w odpowiednim procesie. W ten sposób wszystkie żądania są teraz obsługiwane przez wątki robocze pobierane z puli wątków CLR, a nigdy w wątkach we / wy . " (moje podkreślenie)
Jeff Sternal,
Hmmm, nadal nie jestem do końca pewien ... Ten artykuł pochodzi z czerwca 2003 r. Jeśli czytasz ten z maja 2004 r. (Wprawdzie wciąż dość stary), mówi: „Strona testowa Sleep.aspx może służyć do przechowywania ASP Wątek .NET I / O zajęty ”, gdzie Sleep.aspx po prostu powoduje uśpienie aktualnie wykonywanego wątku: msdn.microsoft.com/en-us/library/ms979194.aspx - Kiedy będę miał szansę, zobaczę jeśli uda mi się zakodować ten przykład i przetestować w usługach IIS 7 i .NET 3.5
Michael Hart
Tak, tekst tego akapitu jest zagmatwany. W dalszej części tej sekcji znajduje się łącze do tematu pomocy technicznej ( support.microsoft.com/default.aspx?scid=kb;EN-US;816829 ), który wyjaśnia pewne kwestie: uruchamianie żądań w wątkach zakończenia operacji we / wy to .NET Framework 1.0 problem rozwiązany w pakiecie zbiorczym poprawek ASP.NET 1.1 czerwca 2003 r. (po którym „WSZYSTKIE żądania są teraz uruchamiane w wątkach roboczych”). Co ważniejsze, ten przykład pokazuje dość wyraźnie, że pula wątków ASP.NET jest tą samą pulą wątków udostępnianą przez System.Threading.ThreadPool.
Jeff Sternal
1

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.

  1. Żądanie dotyczy jakiejś strony
  2. Html jest zwracany
  3. Html nakazuje klientowi wykonanie dalszych żądań (js, css, obrazy itp.)
  4. Dalsze informacje zostaną zwrócone

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.

Wątki a proces

Zarówno wątki, jak i procesy są metodami zrównoleglania aplikacji. Jednak procesy są niezależnymi jednostkami wykonawczymi, które zawierają własne informacje o stanie, używają własnych przestrzeni adresowych i współdziałają ze sobą tylko za pośrednictwem mechanizmów komunikacji międzyprocesowej (zwykle zarządzanej przez system operacyjny). Aplikacje są zwykle dzielone na procesy w fazie projektowania, a proces główny jawnie tworzy podprocesy, gdy logiczne oddzielenie istotnych funkcji aplikacji ma sens. Innymi słowy, procesy są konstrukcją architektoniczną.

Z kolei wątek to konstrukcja kodująca, która nie wpływa na architekturę aplikacji. Pojedynczy proces może zawierać wiele wątków; wszystkie wątki w procesie mają ten sam stan i tę samą przestrzeń pamięci i mogą komunikować się ze sobą bezpośrednio, ponieważ mają te same zmienne.

Źródło

Ty.
źródło
3
@Ty, dzięki za wkład, ale dobrze wiem, jak działa serwer WWW i nie jest to tak naprawdę istotne dla pytania - znowu, jak powiedziałem w pytaniu, nie proszę o wskazówki w tym zakresie jako architekt kwestia. Proszę o szczegółowe informacje techniczne. A jeśli chodzi o to, że „problem loggera” powinien już mieć na miejscu proces asynchroniczny - jak myślisz, że proces asynchroniczny powinien być zapisany przez implementację rejestratora?
Michael Hart
0

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ć.

Timbo
źródło
0

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.

bmm6o
źródło
0

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.

var ts = new CancellationTokenSource();
CancellationToken ct = ts.Token;

ParallelOptions po = new ParallelOptions();
            po.CancellationToken = ts.Token;
            po.MaxDegreeOfParallelism = 6; //limit here

 Task.Factory.StartNew(()=>
                {                        
                  Parallel.ForEach(collectionList, po, (collectionItem) =>
                  {
                     //Code Here PostLog(logEvent);
                  }
                });
khayam
źródło