I think I need some help understanding how static objects persist in an ASP.Net application. I have this scenario:
someFile.cs in a class library:
public delegate void CustomFunction();
public static class A {
public static CustomFunction Func = null;
}
someOtherFile.cs in a class library:
public class Q {
public Q() {
if (A.Func != null) {
A.Func();
}
}
}
Some ASP.Net page:
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();
}
The idea is that I have a business object which does some operation based on a callback function at the UI level. I set the callback function to a static variable on Page_Init (in the real code version, in the Master page, if that makes a difference). I thought that every execution of the page, no matter what user session it came from, would go through that function's logic but operate on its own set of data. What seems to be happening instead is a concurrency issue.
If I run one user session, then while it is sleeping between calls to that callback function, start another user session, when the first session comes back from sleeping it picks up the session ID from the second user session. How can this be possible?
Output of 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)
Why is the function's context (meaning, its local data, etc.) being overwritten from one user session to another?
One solution you might consider is using [ThreadStatic].
http://msdn.microsoft.com/en-us/library/system.threadstaticattribute(VS.71).aspx
It will make your statics per thread. There are cavaets however so you should test.
Each request to an asp.net site comes in and is processed on it's own thread. But each of those threads belong to the same application. That means anything you mark as static is shared across all requests, and therefore also all sessions and users.
In this case, the MyFunc
function that's part of your page class is copied over top of the static Func
member in A
with every page_init, and so every time any user does a page_init, he's replacing the A.Func
used by all requests.
Static data is shared among the entire application domain of your webapp.
In short, it's shared among all the threads serving requests in your webapp, it's not bound to a session/thread/user in any way but to the webapp as a whole.(unlike e.g. php where each request lives in its own isolated environment bar a few knobs provided - such as the session variable.)
I won't try to improve on the other answers' explanations of static members, but do want to point out another way to code around your immediate problem.
As a solution, you could make an instance-oriented version of your class A
, store it in a page-level variable, and pass it to Q
's constructor on page load:
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..
}
}
In fact, if there's no pressing need to declare A2
earlier, you could just instantiate it when you create your instance of Q
in Page_Load
.
Edit: to answer the question you raised in other comments, the reason the variables are being shared is that the requests are sharing the same delegate, which has only a single copy of its variables. See Jon Skeet's The Beauty of Closures for more details.
If you want the data to persist only for the current request, use HttpContext.Items
:
http://msdn.microsoft.com/en-us/library/system.web.httpcontext.items.aspx
If you want the data to persist for the current user's session (assuming you have session state enabled), use HttpContext.Session
:
http://msdn.microsoft.com/en-us/library/system.web.httpcontext.session.aspx