8

我想我需要一些帮助来理解静态对象如何在 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();
}

这个想法是我有一个业务对象,它基于 UI 级别的回调函数执行一些操作。我将回调函数设置为 Page_Init 上的静态变量(在实际代码版本中,在母版页中,如果这有所不同)。我认为页面的每次执行,无论它来自哪个用户会话,都将通过该函数的逻辑,但对它自己的数据集进行操作。相反,似乎正在发生的是并发问题。

如果我运行一个用户会话,那么当它在调用该回调函数之间睡眠时,启动另一个用户会话,当第一个会话从睡眠中恢复时,它会从第二个用户会话中获取会话 ID。这怎么可能?

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)

为什么函数的上下文(意思是其本地数据等)会从一个用户会话覆盖到另一个用户会话?

4

5 回答 5

12

每个对 asp.net 站点的请求都会进入并在它自己的线程上进行处理。但是这些线程中的每一个都属于同一个应用程序。这意味着您标记为静态的任何内容都将在所有请求之间共享,因此也将在所有会话和用户之间共享。

在这种情况下,作为MyFunc页面类一部分的函数被复制到每个 page_init 中的静态Func成员A之上,因此每次任何用户执行 page_init 时,他都会替换所有请求 A.Func使用的。

于 2009-09-16T19:49:28.693 回答
4

静态数据在 webapp的整个应用程序域之间共享。简而言之,它在您的 web 应用程序中为请求提供服务的所有线程之间共享,它不以任何方式绑定到会话/线程/用户,而是绑定到整个 web 应用程序。(不像例如 php,每个请求都存在于自己的隔离环境栏中提供了一些旋钮 - 例如会话变量。)

于 2009-09-16T19:50:45.320 回答
4

我不会尝试改进其他答案对静态成员的解释,但确实想指出另一种解决您当前问题的编码方式。

作为一种解决方案,您可以制作一个面向实例的 class 版本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,您可以在创建 in 的实例时实例化QPage_Load

编辑:回答您在其他评论中提出的问题,共享变量的原因是请求共享同一个委托,该委托只有一个变量副本。有关更多详细信息,请参阅 Jon Skeet 的闭包之美。

于 2009-09-16T20:03:19.253 回答
2

您可能考虑的一种解决方案是使用 [ThreadStatic]。

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

它将使您的每个线程都静态。但是有一些警告,因此您应该进行测试。

于 2009-09-16T19:51:50.260 回答
2

如果您希望数据仅针对当前请求保留,请使用:http HttpContext.Items: //msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx

如果您希望数据为当前用户的会话保留(假设您启用了会话状态),请使用:http HttpContext.Session: //msdn.microsoft.com/en-us/library/system.web.httpcontext.session.aspx

于 2009-09-16T20:27:25.170 回答