【问题标题】:Access session data from another thread从另一个线程访问会话数据
【发布时间】:2012-10-23 18:33:03
【问题描述】:

我有一个问题。在我的网络应用程序中,我有一个页面启动另一个线程来执行耗时的任务。在这个新线程中,我调用了我的一种架构方法(在另一个项目中 - 一个架构项目)。问题是:在其中一种方法中,我访问了 HttpContext.Current.Session 字段。但是当我启动应用程序时,会抛出一个异常,说这个对象 (HttpContext.Current.Session) 有一个空引用。我如何将新线程的上下文设置为与 HttpApplication 上下文相同以访问 HttpContext.Current.Session

【问题讨论】:

    标签: c# asp.net .net


    【解决方案1】:

    这里有很多事情需要考虑。

    如果您的线程的生命周期等于页面的生命周期并且您需要大量随机访问HttpSessionState,那么您应该从使用静态@ 创建后台线程的调用中获取SynchronizationContext 987654323@.

    一旦你有了它,你可以将它传递给你的线程,然后当你需要访问与请求相关联的HttpContextBase 上的任何东西时(包括会话),你可以调用Post method 上的 SynchronizationContext 您传递给线程以获取值(或设置它们):

    // From thread servicing request.
    var sc = SynchronizationContext.Current;
    
    // Run the task
    Task t = Task.Run(() => {
        // Do other stuff.
        // ...
    
        // The value to get from the session.
        string sessionValue = null;
    
        // Need to get something from the session?
        sc.Post(() => {
            // Get the value.
            sessionValue = HttpContext.Current.Session["sessionValue"];
        }
    
        // Do other stuff.
        // ...
    });
    

    这样做很重要,因为对HttpContextBase(以及与之相关的任何内容)的访问是线程安全的,并且与处理请求的线程(嗯,上下文)相关联.

    注意Post 方法不会阻塞,因此调用Post 之后的代码(即// Do other stuff. 之后的行)应该独立于传递给Post 的委托。如果后面的代码是依赖的,你需要等待调用完成才能继续,那么你可以调用Send method;它具有相同的签名,并且会一直阻塞,直到委托中的代码被执行。

    也就是说,如果您只想只读访问这些值,那么最好在调用代码之前获取它们,然后在您的代码中访问它们后台线程:

    // Get the values needed in the background thread here.
    var values = {
        SessionValue = HttpContext.Current.Session["sessionValue"];
    };
    
    // Run the task
    Task t = Task.Run(() => {
        // Do other stuff.
        // ...
    
        // Work with the session value.
        if (values.SessionValue == ...)
    
        // Do other stuff.
        // ...
    });
    

    如果您的线程要在请求得到处理后继续处理,那么您只有处于只读状态,并且您必须在开始之前捕获它线程。一旦请求得到服务,即使会话存在,它也是一个逻辑概念;根据会话状态的提供者(会话状态管理器、SQL Server 等),每次有新请求进入时,对象都可能会被水合。

    您还必须处理会话超时问题,您甚至不知道会话是否在您想要访问的点存在

    【讨论】:

    • 我用以下信息更新了帖子:在新线程中调用的方法在另一个项目中(一个类库)。这种方法行不通,不是吗?
    • 不,此时您遇到了设计问题。您应该提供接受SynchronizationContext 的异步方法,以便在您需要访问HttpContext 时回调。一般来说,如果可以避免的话,应该避免在异步操作中使用HttpContextBaseCurrent 属性。如您所见,您必须通过整个调用堆栈线程化同步上下文才能从另一个线程访问这些变量。
    • 您似乎认为 sc.Post 会立即执行。在“旧版”asp.net 中确实如此,它确实会立即执行,但是 Post 的想法是它也可以在线程池上执行。所以我不会依赖“做其他事情”部分中的会话值。
    • @Marcus 一个简单的疏忽。通过在第一个代码示例之后的第二段中添加对 PostSend 之间差异的解释以及您想要使用它们的时间进行更正。
    【解决方案2】:

    如果将当前上下文传递给子线程,问题是它依赖于父上下文。如果父上下文终止,那么您的线程将无法再访问该上下文并且会导致问题。

    一种解决方案是克隆父上下文,然后在线程中使用克隆。这样,如果父线程释放,该线程将继续工作并可以访问所有上下文内容。

    HttpContext ctx = ThreadingFixHttpContext();
    Thread newThread = new System.Threading.Thread(new ThreadStart(() =>
    {
        HttpContext.Current = ctx;
        Thread.CurrentPrincipal = ctx.User;
        var test = HttpContext.Current.Session["testKey"];
    }));
    newThread.Start();
    

    这也应该适用于 Task.Run() 方式。完成工作的方法:

        private static Object CloneObject(Object Source)
        {
            MemoryStream Stream = new MemoryStream();
            System.Runtime.Serialization.Formatters.Binary.BinaryFormatter Formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
            Formatter.Serialize(Stream, Source);
            Stream.Position = 0;
            object Clone = (object)Formatter.Deserialize(Stream);
            Stream.Close(); Stream.Dispose();
            return Clone;
        }
    
        public static System.Web.HttpContext ThreadingFixHttpContext()
        {
            //If this method is called from a new thread there is issues holding httpContext.current (which is injected from parent thread in AniReturnedPaymentsFetch.ascx.cs
            //The parent http current will die of its own accord (because it is from a different thread)
            //So we clone it into thread current principal. 
            System.Security.Principal.WindowsIdentity ThreadIdentity =
                (System.Security.Principal.WindowsIdentity)CloneObject(System.Web.HttpContext.Current.User.Identity);
    
            //Then create a new httpcontext using the parent's request & response, so now the http current belongs to this thread and will not die.
            var request = System.Web.HttpContext.Current.Request;
            var response = System.Web.HttpContext.Current.Response;
            var ctx = new System.Web.HttpContext(request, response);
            ctx.User = new System.Security.Principal.WindowsPrincipal(ThreadIdentity);
            return ctx;
        }
    

    【讨论】:

    • Type 'System.Web.HttpContext' in Assembly 'System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' 未标记为可序列化。
    • 这是目前在企业环境中使用的,所以它确实有效。我从来没有遇到过你的错误。
    【解决方案3】:

    您无法从线程中访问会话,但您可以使用以下方式共享您的数据:HttpRuntime.Cache

    但有几件事要记住:与会话不同,缓存确实会过期。此外,缓存在所有网络用户之间共享。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-17
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多