Pytanie Korzystanie z HTTPContext w wątkach


Użytkownik odbiera stronę spawn.aspx, która następnie spawnuje sześć wątków, wyświetlając wszystkie strony

 ((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn's HTTPContext);

Nie martw się o to, że ASP.Net pozornie wysyła 7 odpowiedzi użytkownika na jedno żądanie, ta część jest obsługiwana i wysyłana jest tylko jedna odpowiedź.

Problem polega na tym, że w środowisku o dużym natężeniu ruchu (naszym środowisku produkcyjnym) z wieloma wątkami (quad-quady) pojawia się błąd:

System.IndexOutOfRangeException 
at System.collections.ArrayList.Add 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, DateTime utcDepTime) 
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, String requestVritualPath)
at System.Web.UI.Page.AddWrappedFileDependencies(Object virtualFileDependencies) 
at ASP.spawned_page_no_1_aspx.FrameworkInitialize()
at System.Web.UI.Page.ProcessRequest

Nie możemy go powielić gdzie indziej. Mój współpracownik uważa, że ​​dzieje się tak dlatego, że ponownie wykorzystuję oryginalny tekst HTTPContext i przekazuję go innym wątkom oraz że nie jest bezpieczny dla wątków.

Zgodnie z tą logiką próbowałem utworzyć nowy tekst HTTPContext, aby przejść do wątków. Ale jego części na pozór nie "łączą się". W szczególności muszę uzyskać obiekt Session w nowym kontekście HTTPContext. Wyobrażam sobie, że chciałbym zdobyć także inne części, takie jak Cache. Dla rekordu HTTPContext.Current.Session.IsSynchronized jest fałszem.

Moje pytania to:

  1. Czy uważasz, że błąd wynika z używania HTTPContext w wątkach?
  2. Jak mogę to naprawić?
  3. Jeśli poprawka duplikuje tekst HTTPContext dla każdego wątku, w jaki sposób mogę uzyskać sesję (i pamięć podręczną) w nowej? Żądanie i odpowiedź przychodzą w ctor, ale sesji nie można ustawić.

Edycja: więcej szczegółów

Wracając do tego stwierdzenia: "Nie przejmuj się faktem, że ASP.Net pozornie wysyła 7 odpowiedzi użytkownika na 1 żądanie, ta część jest obsługiwana i wysyłana jest tylko jedna odpowiedź." Ogromny fan Raymond Chen, zgadzam się z tobą: "Teraz masz dwa problemy" to uzasadnione stwierdzenie w przypadku braku dalszych informacji.

Co się właściwie dzieje, to że buduję dokument Excel, aby go odesłać. Na stronie spawn.aspx ustawiane są pewne informacje o stanie, w tym informacja o renderowaniu do programu Excel oraz obiekt do renderowania. Każda spawnowana strona otrzymuje tę informację i będzie blokować, dopóki nie przyjdzie kolej na renderowanie do obiektu. Jeśli dosłownie wygląda to tak:

 protected override void Render(System.Web.UI.HtmlTextWriter writer)
 {
    if (this.RenderToExcel)
    {
      Deadlocker.SpinUntilCurrent(DeadLockToken);
      RenderReport(this, this.XLSWriter);
      Deadlocker.Remove(DeadLockToken);
    }
    else
      base.Render(writer);
 }

Ale całe przetwarzanie do tego momentu - dostęp do bazy danych, kontrola heirarchii, wszystko to odbywa się równolegle. I jest ich dużo - wystarczy, że paralizują go, a jednocześnie pozwalając mu blokować Render, skróci ogólny czas w ponad połowie.

A najlepsze jest to, że nic nie trzeba było przepisywać na potrzeby renderowania Excela. Wszystkie kontrolki wiedzą, jak się wyrenderować, i możesz odwiedzać każdą odrodzoną stronę niezależnie (w rzeczywistości jest to normalny przypadek - raport Excela to tylko agregacja wszystkich zrekrutowanych stron).

Tak więc doszedłem do wniosku, że efekt końcowy będzie "nie możesz tego zrobić, musisz przemyśleć podejście" - ale musiałem przynajmniej spróbować, ponieważ wszystko działa tak ładnie, nie powielając żadnej logiki lub kodu abstrakcja czegokolwiek jest po prostu idealna. I to jest tylko wielowątkowość, to jest problem, jeśli renderuję strony seryjnie wszystko jest w porządku, po prostu powolne.


12
2018-04-09 15:51


pochodzenie




Odpowiedzi:


Podczas gdy HttpContext jest zaprojektowany do obsługi kontekstu, który nie jest specyficzny dla wątku (ponieważ kontekst http może zaczynać się od jednego wątku, a kończyć na innym), nie jest on domyślnie bezpieczny dla wątków.

Zasadniczo problem polega na tym, że robisz coś, co nie jest przeznaczone, te żądania będą generalnie wielokrotne i każdy będzie miał przypisaną własną aplikację HttpApplication, aby spełnić żądanie, a każdy z nich ma swój własny tekst HttpContext.

Naprawdę powinienem spróbować pozwolić infrastrukturze asp.net delegować same żądania.


4
2018-04-09 22:29





Twoi współpracownicy mają rację, jeśli jeden wątek zablokuje zasób i inny wątek spróbuje go użyć, to twoja pula wątków przeżyje bum! Niezbyt dobry wynik. Większość ludzi rozwiązuje to przez tworzenie nowych obiektów i przekazywanie ich do wątków o charakterze paramatycznym. Jeśli absolutnie potrzebujesz użyć tego samego obiektu, musisz zaimplementować kod, który najpierw sprawdza, czy zasób jest używany przez inny wątek, a następnie czeka chwilę zanim przejrzysz ponownie. Przykładem może być utworzenie boolu IsInUse, który zawsze sprawdzasz jako pierwszy, wtedy twój wątek ustawia go na true, jeśli używa tego zasobu, a następnie false, gdy jest to zrobione, uniemożliwiając innym wątkom próbowanie użycia zasobu bazowego (twój httpContext) . Mam nadzieję, że to pomoże.


2
2018-04-09 15:57



Al prawdopodobnie blokowanie tego typu będzie trudne, wyjątek gwintowania, który dostaje, pochodzi z klasy Strona, która mutuje kontekst http, chyba że może przesłonić akcję na stronie, która to robi i umieścić blokadę, a następnie wygrał rozwiązanie blokujące nie działa. - meandmycode
Świetny komentarz, zgadzam się z tobą. Wolałbym przekazać całkowicie nowy obiekt, który czerpie część informacji z kontekstu HTTP w momencie tworzenia bieżnika. To byłby punkt kuloodporny. - Al Katawazi


Ponieważ HttpContext jest częścią biblioteki .Net, oczekiwałbym, że wszystkie statyczne funkcje są wątkowo bezpieczne, ale wszelkie niestatyczne elementy nie są wątkowo bezpieczne podczas wywoływania tego samego wystąpienia obiektu. Więc mam nadzieję, że następnym razem będziesz oczekiwał, że dzielenie się instancją HttpContext przez wątki będzie miało problemy.

Czy istnieje sposób, w jaki można oddzielić operacje, które należy wykonać równolegle od HttpContext? Jeśli po prostu ładują dane i piszą do formatu CSV, ten kod nie ma wymaganej zależności od kontroli użytkownika ASP.NET lub cyklu życia strony. Po usunięciu tej zależności możesz zaimplementować stronę z asynchronicznym HttpHandler, wykonując operacje równoległe między IHttpHandler.BeginProcessingRequest () i IHttpHandler.EndProcessingRequest ().


1
2018-04-11 03:29





Zapewniam, że za każdym razem, gdy uzyskujesz dostęp do kolekcji HttpContext.Current.Items, używasz bloku Blokada monitora (C #) / SyncLock (VB) w obiekcie HttpContext.Current.Items.SyncRoot do zawijania połączeń.


1
2018-02-27 04:00