会话在不同的线程中变为null

时间:2015-04-15 14:12:27

标签: asp.net-mvc session

我有一个在不同线程中运行的任务,需要会话。我已经完成了:

public GenerateDocList(LLStatistics.DocLists.DocList docs)
        {
            this.docs = docs;

            context = HttpContext.Current;
        }

public void StartTask()
        {
            //this code runs in a separate thread
            HttpContext.Current = context;
            /* rest of the code */
        }

现在线程知道会话并且它有效一段时间但是在我的循环HttpContext.Current.Session中的某个时刻变为空。我有什么想法可以做些什么吗?

public static LLDAC.DAL.DBCTX LLDB
        {
            get
            {
                LLDAC.DAL.DBCTX currentUserDBContext = HttpContext.Current.Session["LLDBContext"] as LLDAC.DAL.DBCTX;
                if (currentUserDBContext == null)
                {
                    currentUserDBContext = new LLDAC.DAL.DBCTX();
                   HttpContext.Current.Session.Add("LLDBContext", currentUserDBContext);//this works only for a few loop iterations
                }
                return currentUserDBContext;
            }
        }

2 个答案:

答案 0 :(得分:0)

通常,这对于多线程操作来说是一个非常脆弱的模式。长时间运行的任务(我假设这是)最适合类中的实例方法而不是静态方法,以便类可以维护任何依赖对象。此外,由于会话状态不是线程安全的,并且可以跨越多个请求,您将通过在会话中兑现您的数据库上下文来进入一些非常危险的业务。

如果您确信最好使用静态方法并将其存储在会话中,您可以执行以下操作:

public static HttpSessionState MySession { get; set; }

public GenerateDocList(LLStatistics.DocLists.DocList docs)
{
    this.docs = docs;

    MySession = HttpContext.Current.Session;
}

然后:

    public static LLDAC.DAL.DBCTX LLDB
    {
        get
        {
            LLDAC.DAL.DBCTX currentUserDBContext = MySession["LLDBContext"] as LLDAC.DAL.DBCTX;
            if (currentUserDBContext == null)
            {
                currentUserDBContext = new LLDAC.DAL.DBCTX();
                if (MySession == null)
                {
                    thow new InvalidOperaionException("MySession is null");
                }
                MySession.Add("LLDBContext", currentUserDBContext);
            }
            return currentUserDBContext;
        }
    }

请注意,您仍然可能遇到会话问题,因为其他线程仍然可以修改会话。

更好的解决方案可能看起来像这样:

public class DocListGenerator : IDisposable
{
    public LLDAC.DAL.DBCTX LLDB { get; private set; }
    public DocListGenerator()
    {
        LLDB = new LLDAC.DAL.DBCTX();
    }

    public void GenerateList()
    {
        // Put loop here.
    }

    public void Dispose()
    {
        if (LLDB != null)
        {
            LLDB.Dispose();
        }
    }
}

然后您的调用代码如下所示:

public void StartTask()
{
    using (DocListGenerator generator = new DocListGenerator()
    {
        generator.GenerateList();
    }
}

如果你真的想要缓存某些东西,你可以像这样缓存你的实例:

HttpContext.Current.Sesssion.Add("ListGenerator", generator);

但是,我仍然不认为这是一个特别好的主意,因为你的上下文仍然可以通过不同的线程处理或以其他方式改变。

答案 1 :(得分:0)

在主要的Request线程之外的任何地方使用与HttpContext.Current相关的任何东西通常会让你在ASP.net中遇到麻烦。

HttpContext实际上是在属于线程池的线程上备份的,并且线程很可能会在另一个请求上重用。

这实际上是在ASP.net中使用新的Async / Await关键字的常见问题。

为了帮助你,首先要知道你为什么要这样做?

  1. 这是单个服务器还是具有多个负载平衡服务器的Web场?
  2. 您是自己托管,还是由提供商托管的网站?
  3. 什么是SessionState实现(SQL Server,状态服务器,进程内或类似MemCached,Redis等自定义...)
  4. 什么版本的ASP .net?
  5. 为什么要启动新线程而不是仅仅在请求线程上进行处理?

  6. 如果你真的不能(或不应该)使用会话。然后你可以使用类似相关ID的东西。

    Guid correlationID = Guid.NewGuid();

    HttpContext.Current.Session [“DocListID”] = correlationID;

    DocList.GoOffAndGenerateSomeStuffOnANewThread(的correlationID);

    ...当进程完成后,使用指定的ID

    将结果存储在某处

    //将结果序列化为SQL服务器,文件系统,缓存... DocList.StoreResultsSomewhereUnderID();

    ......稍后

    DocList.CheckForResultsUnderID(HttpContext.Current.Session [ “DocListID”]);