Почему в моей статической функции ASP.Net происходит "контекстное" пересечение между сеансами пользователя? - PullRequest
7 голосов
/ 16 сентября 2009

Мне кажется, мне нужна помощь в понимании того, как статические объекты сохраняются в приложении ASP.Net. У меня есть такой сценарий:

someFile.cs в библиотеке классов:

public delegate void CustomFunction();

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

someOtherFile.cs в библиотеке классов:

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

Некоторые страницы 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();
}

Идея состоит в том, что у меня есть бизнес-объект, который выполняет некоторую операцию на основе функции обратного вызова на уровне пользовательского интерфейса. Я установил функцию обратного вызова для статической переменной в Page_Init (в реальной версии кода, на главной странице, если это имеет значение). Я думал, что каждое выполнение страницы, независимо от того, с какого пользовательского сеанса она идет, будет проходить через логику этой функции, но работать с собственным набором данных. Кажется, что вместо этого происходит проблема параллелизма.

Если я запускаю один пользовательский сеанс, то, пока он спит между вызовами этой функции обратного вызова, запускает другой пользовательский сеанс, когда первый сеанс возвращается из спящего режима, он получает идентификатор сеанса второго сеанса пользователя. Как это может быть возможно?

Вывод 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)

Почему контекст функции (т.е. ее локальные данные и т. Д.) Перезаписывается от одного сеанса пользователя к другому?

Ответы [ 5 ]

11 голосов
/ 16 сентября 2009

Каждый запрос на сайт asp.net приходит и обрабатывается в своем собственном потоке. Но каждый из этих потоков принадлежит одному приложению. Это означает, что все, что вы помечаете как статическое, используется всеми запросами, а также всеми сеансами и пользователями.

В этом случае функция MyFunc, являющаяся частью класса вашей страницы, копируется поверх статического члена Func в A с каждым page_init, и поэтому каждый раз, когда любой пользователь делает page_init, он замена A.Func, используемого всеми запросами.

4 голосов
/ 17 сентября 2009

Я не буду пытаться улучшить объяснения статических членов в других ответах, но хочу указать другой способ кодирования вашей непосредственной проблемы.

В качестве решения вы можете сделать экземпляр-ориентированную версию вашего класса A, сохранить ее в переменной уровня страницы и передать ее конструктору Q при загрузке страницы:

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

На самом деле, если нет срочной необходимости объявить A2 ранее, вы можете просто создать его экземпляр, когда создадите свой экземпляр Q в Page_Load.

Редактировать: чтобы ответить на вопрос, который вы подняли в других комментариях, причина, по которой переменные являются общими, заключается в том, что запросы совместно используют один и тот же делегат, у которого только одна копия его переменных. Подробнее см. «1015 * Красота замыканий» Джона Скита .

4 голосов
/ 16 сентября 2009

Статические данные распределяются по всему домену приложения вашего веб-приложения. Короче говоря, он распределяется между всеми потоками, обслуживающими запросы в вашем веб-приложении, он никак не связан с сессией / потоком / пользователем, а с веб-приложением в целом (в отличие, например, от php, где каждый запрос находится в отдельной изолированной панели среды). предусмотрено несколько ручек - например, переменная сеанса.)

2 голосов
/ 16 сентября 2009

Одним из решений, которое вы могли бы рассмотреть, является использование [ThreadStatic].

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

Это сделает вашу статику за поток. Однако есть каваэты, поэтому вы должны проверить.

1 голос
/ 17 сентября 2009

Если вы хотите, чтобы данные сохранялись только для текущего запроса, используйте HttpContext.Items: http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx

Если вы хотите, чтобы данные сохранялись в течение сеанса текущего пользователя (если у вас включено состояние сеанса), используйте HttpContext.Session: http://msdn.microsoft.com/en-us/library/system.web.httpcontext.session.aspx

...