2009-09-16 22 views
7

Myślę, że potrzebuję pomocy w zrozumieniu, w jaki sposób statyczne obiekty pozostają w aplikacji ASP.Net. Mam następujący scenariusz:Dlaczego moja funkcja statyczna ASP.Net "kontekst" skrzyżowania między sesjami użytkownika?

someFile.cs w bibliotece klasy:

public delegate void CustomFunction(); 

public static class A { 
    public static CustomFunction Func = null; 
} 

someOtherFile.cs w bibliotece klasy:

public class Q { 
    public Q() { 
     if (A.Func != null) { 
      A.Func(); 
     } 
    } 
} 

Niektóre strona ASP.Net:

Page_Init { 
    A.Func = MyFunc; 
} 

public void MyFunc() { 
    System.IO.File.AppendAllText(
     "mydebug.txt", DateTime.Now.ToString("hh/mm/ss.fff", Session.SessionID)); 
} 

Page_Load { 
    Q myQ = new Q(); 
    System.Threading.Thread.Sleep(20000); 
    mQ = new Q(); 
} 

Chodzi o to, że mam obiekt biznesowy, który wykonuje pewne operacje w oparciu o funkcję zwrotną na poziomie interfejsu użytkownika. Ustawiam funkcję zwrotną na statyczną zmienną w Page_Init (w prawdziwej wersji kodu, na stronie wzorcowej, jeśli to robi różnicę). Sądziłem, że każde wykonanie strony, bez względu na to, z jakiej sesji użytkownika pochodzi, przejdzie przez logikę tej funkcji, ale będzie działać na własnym zestawie danych. Zamiast tego wydaje się, że jest to kwestia współbieżności.

Jeśli uruchomię jedną sesję użytkownika, to podczas snu między połączeniami do tej funkcji oddzwaniania, rozpocznij sesję innego użytkownika, kiedy pierwsza sesja wraca z trybu uśpienia, odbiera identyfikator sesji z sesji drugiego użytkownika. Jak to możliwe?

Wyjście mydebug.txt:

01/01/01.000 abababababab (session #1, first call) 
01/01/05.000 cdcdcdcdcdcd (session #2, first call - started 5 seconds after session #1) 
01/01/21.000 cdcdcdcdcdcd (session #1 returns after the wait but has assumed the function context from session #2!!!!!) 
01/01/25.000 cdcdcdcdcdcd (session #2 returns with its own context) 

Dlaczego kontekst danej funkcji (czyli jego lokalne dane, itd.) Są zastępowane z jednej sesji użytkownika do innej?

+0

Dziękuję wszystkim za pomoc. Zasady StackOverflow. :) –

Odpowiedz

2

Jednym z rozwiązań, które można rozważyć, jest użycie [ThreadStatic].

http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(VS.71).aspx

Będzie dokonać statykę na wątku. Są jednak cavaets, więc powinieneś przetestować.

+0

Poza notatką MSDN w twoim linku o wartości początkowej, jakie są zastrzeżenia? –

+0

To nie zadziała w ASP.NET. Nie można przewidzieć, który wątek będzie obsługiwał dane żądanie. – Brannon

+0

@Brannon - ale czy nie mamy gwarancji, że pojedynczy wątek obsłuży jedno żądanie? Myślę, że to jest mój problem - ten jeden wątek myFunc został zmieniony w trakcie operacji przez przypisanie mojego wątku do innego wątku. –

11

Każde żądanie do strony asp.net przychodzi i jest przetwarzane w jego wątku. Ale każda z tych wątków należy do tej samej aplikacji. Oznacza to, że wszystko, co oznaczysz jako statyczne, jest współużytkowane we wszystkich żądaniach, a więc również w przypadku wszystkich sesji i użytkowników.

W tym przypadku MyFunc funkcja to część swojej klasie strona jest kopiowana na górze statycznej Func członka w A z każdym Page_Init, a więc za każdym razem, każdy użytkownik robi Page_Init, on zastępującA.Func wykorzystywane przez wszystkie żądania.

+0

Rozumiem, że adres funkcji jest zastępowany. Nie rozumiem jednak, dlaczego dane, do których uzyskuję dostęp w funkcji, również przechodzą. Dlaczego sesja numer 1, która ma Session.SessionID = abababababab, podczas ponownego wywoływania myFunc, nagle ma Session.SessionID = cdcdcdcdcd? –

+0

Ponieważ delegat przechwytuje całe środowisko tej metody, w tym bieżący obiekt sesji, po przypisaniu go do statycznego delegata. – nos

+0

Ponieważ przed sesją nr 1 wywoływana jest statyczna metoda A.Func() w sesji konstruktora Q(), sesja # 2 zastąpiła tę funkcję własną implementacją. Wywołujesz całkowicie inną funkcję po raz drugi. –

4

Dane statyczne są udostępniane całej aplikacji application domain swojej aplikacji internetowej. W skrócie, jest on dzielony pomiędzy wszystkie wątki obsługujące żądania w twojej aplikacji webowej, nie jest związany w żaden sposób z sesją/wątkiem/użytkownikiem, ale z webappem jako całością (w przeciwieństwie do php, gdzie każde żądanie żyje w odizolowanym środowisku pasek kilka dostarczonych pokręteł - takich jak zmienna sesji.)

+0

Próbuję poprawnie zwerbalizować w moim komentarzu do Joela, rozumiem, dlaczego odwołanie do funkcji jest współużytkowane w domenie aplikacji. Nie rozumiem, dlaczego zmienne, które SĄ sesja/wątek/użytkownik związane w logice funkcji są również udostępniane w domenie aplikacji. –

4

Nie będę starał się ulepszać wyjaśnień innych statycznych członków, ale chcę wskazać inny sposób kodowania najbliższego problemu.

Jako rozwiązanie, można zrobić instancją zorientowanych wersję swojej klasie A, należy go przechowywać w zmiennej na poziomie strony i przekazać go do konstruktora Q „s na stronie obciążenia:

public class MyPage: Page { 
    private A2 _a2; 

    // I've modified A2's constructor here to accept the function 
    protected Page_Init() { this._a2 = new A2(MyFunc); } 

    protected Page_Load() { 
     Q myQ = new Q(this._a2); 
     // etc.. 
    } 
} 

W Faktem jest, że jeśli nie ma potrzeby wcześniejszego zadeklarowania wartości A2, można po prostu utworzyć instancję podczas tworzenia instancji Q w Page_Load.

Edit: odpowiedzieć na pytanie, jesteś wychowany w innych uwag, powodem zmienne są wspólne jest to, że wnioski są w obrębie tego samego delegata, który ma tylko jedną kopię swoich zmiennych. Zobacz Jon Skeet's The Beauty of Closures po więcej szczegółów.

+0

DOH !!!!!!! Teraz ma dla mnie sens. Nie zdawałem sobie nawet sprawy, że w tej sprawie zamknąłem zamknięcie. Mój przykładowy kod jest niezwykle uproszczoną wersją prawdziwego kodu - twoje zaproponowane rozwiązanie niestety nie pasuje do prawdziwego przypadku. Ale dziękuję za wyjaśnienie zamknięcia. –

Powiązane problemy