为什么我的ASP.Net静态函数';s";上下文“;用户会话之间的交叉?
我想我需要一些帮助来理解ASP.Net应用程序中静态对象是如何持久存在的。我有这样一个场景: 类库中的someFile.cs:为什么我的ASP.Net静态函数';s";上下文“;用户会话之间的交叉?,asp.net,static-methods,Asp.net,Static Methods,我想我需要一些帮助来理解ASP.Net应用程序中静态对象是如何持久存在的。我有这样一个场景: 类库中的someFile.cs: public delegate void CustomFunction(); public static class A { public static CustomFunction Func = null; } public class Q { public Q() { if (A.Func != null) {
public delegate void CustomFunction();
public static class A {
public static CustomFunction Func = null;
}
public class Q {
public Q() {
if (A.Func != null) {
A.Func();
}
}
}
类库中的someOtherFile.cs:
public delegate void CustomFunction();
public static class A {
public static CustomFunction Func = null;
}
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)
为什么函数的上下文(意思是它的本地数据等)会从一个用户会话覆盖到另一个用户会话?对asp.net站点的每个请求都会进入并在它自己的线程上处理。但是这些线程中的每一个都属于同一个应用程序。这意味着您标记为静态的任何内容都将在所有请求中共享,因此也将在所有会话和用户中共享
在这种情况下,作为page类一部分的
MyFunc
函数会在A
中的静态Func
成员上复制到每个页面,因此每次用户进行页面初始化时,他都会替换所有请求使用的A.Func
静态数据在整个webapp中共享。
简而言之,它是在Web应用程序中请求服务的所有线程之间共享的,它不是以任何方式绑定到会话/线程/用户,而是作为WebApp作为一个整体。(不像PHP,每个请求都存在于它自己的隔离环境栏中,提供了几个旋钮——例如会话变量)。 < P>您可以考虑使用一个解决方案[线程静态]。
它将使您的静态每个线程。但是,有一些警告,所以您应该进行测试。我不会试图改进其他答案对静态成员的解释,但我想指出另一种解决当前问题的方法 作为一种解决方案,您可以制作类
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
,您可以在Page\u Load
中创建Q
的实例时实例化它
编辑:要回答您在其他评论中提出的问题,共享变量的原因是请求共享同一个委托,该委托只有一个变量副本。有关更多详细信息,请参阅Jon Skeet的。如果您希望数据仅用于当前请求,请使用
HttpContext。Items
:
如果希望当前用户会话的数据保持不变(假设已启用会话状态),请使用
HttpContext.session
:
我确实知道功能地址正在被替换。但是,我不明白为什么函数中访问的数据也会发生交叉。为什么具有session.SessionID=abababab的session#1在再次调用myFunc时突然具有session.SessionID=cdcdcd?因为在将当前会话对象分配给静态委托时,委托捕获该方法的整个环境,包括当前会话对象。因为在session#1调用静态A.Func()之前构造函数会话#2中的方法将该函数替换为它自己的实现。你在第二轮调用一个完全不同的函数。它是通过闭包来实现的。+1杰夫提到闭包时,灯为我发出咔哒声。感谢您的回答和评论。除了链接中有关初始值的MSDN注释外,还有哪些注意事项?这在ASP.NET中不起作用。您无法预测哪个线程将为给定的请求提供服务。ThreadStatic变量的另一种替代方法是HttpContext.Current.Items,它的作用域是一个不同的请求(例如,请参阅)。@Matt-通常,单个线程将为请求提供服务。ThreadStatic的问题是,在请求结束后,这些值将持续存在于该线程上。虽然这在您的场景中可能有效,但它不是您想要的。使用HttpContext.Items。请记住:在会话过程中,同一个用户可能会出现在许多不同的线程上。就像我试图在对Joel的评论中正确表述一样,我理解为什么在应用程序域中共享函数引用。我不明白为什么函数逻辑中的会话/线程/用户绑定变量也在应用程序域中共享。DOH!!!!!!!现在我完全明白了。我甚至没有意识到我已经在这个案子中设定了一个了结。我的示例代码是实际代码的一个极其简化的版本——不幸的是,您提出的解决方案不适合实际情况。但是非常感谢结束语的解释。谢谢大家的帮助。堆栈溢出规则。:)