为什么我的ASP.Net静态函数的“上下文”在用户会话之间交叉?

时间:2020-12-19 16:49:14

I think I need some help understanding how static objects persist in an ASP.Net application. I have this scenario:

我想我需要一些帮助来理解静态对象如何在ASP.Net应用程序中持续存在。我有这种情况:

someFile.cs in a class library:

类库中的someFile.cs:

public delegate void CustomFunction();

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

someOtherFile.cs in a class library:

类库中的someOtherFile.cs:

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

Some ASP.Net page:

一些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();
}

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.

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

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?

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

Output of mydebug.txt:

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?

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

5 个解决方案

#1


2  

One solution you might consider is using [ThreadStatic].

您可以考虑使用[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.

它将使每个线程的静态。然而有cavaets你应该测试。

#2


11  

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.

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

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.

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

#3


4  

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

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

#4


4  

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:

作为解决方案,您可以创建类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..
    }
}

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.

实际上,如果没有迫切需要提前声明A2,那么当您在Page_Load中创建Q实例时,可以实例化它。

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.

编辑:为了回答您在其他评论中提出的问题,共享变量的原因是请求共享同一个委托,该委托只有一个变量副本。有关详细信息,请参阅Jon Skeet的The Beauty of Closures。

#5


1  

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

如果您希望数据仅保留当前请求,请使用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

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

#1


2  

One solution you might consider is using [ThreadStatic].

您可以考虑使用[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.

它将使每个线程的静态。然而有cavaets你应该测试。

#2


11  

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.

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

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.

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

#3


4  

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

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

#4


4  

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:

作为解决方案,您可以创建类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..
    }
}

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.

实际上,如果没有迫切需要提前声明A2,那么当您在Page_Load中创建Q实例时,可以实例化它。

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.

编辑:为了回答您在其他评论中提出的问题,共享变量的原因是请求共享同一个委托,该委托只有一个变量副本。有关详细信息,请参阅Jon Skeet的The Beauty of Closures。

#5


1  

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

如果您希望数据仅保留当前请求,请使用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

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