会话状态异常的意外行为

时间:2014-09-16 11:27:33

标签: c# asp.net session async-await asp.net-4.5

我正在ASP.NET MVC控制器方法的上下文中调查asyncawait,并且我得到了一些意想不到的行为。

我有以下控制器类:

[SessionState(System.Web.SessionState.SessionStateBehavior.Required)]
public class HomeController : Controller
{
    // GET: Home
    public ActionResult Index()
    {
        return View();
    }

    public async Task<ActionResult> Check1()
    {
        Session["Test"] = "Check";
        System.Diagnostics.Debug.WriteLine("Session: " + Session["Test"]);
        await Task.Delay(20000);
        return View();
    }

    public ActionResult Check2()
    {
        var test = Session["Test"];
        ViewBag.Test = test;
        return View();
    }
}

每个方法Check1和Check2的简单视图:

检查1

@{
    ViewBag.Title = "Check1";
}

<h2>View with Delay</h2>

CHECK2

@{
    ViewBag.Title = "Check2";
    var check = ViewBag.Test;
}

<h2>Immediate View @check</h2>

现在,在启动应用程序后,当我在一个选项卡中打开http://localhost:23131/Home/Check1而在第二个选项卡中打开http://localhost:23131/Home/Check2时,对Check1的调用会在预期的20秒后返回,并致电{ {1}}立即返回,但它没有在会话状态中设置值,我不明白为什么。它应该立即设置,因为它是在延迟之前。但是,输出窗口上会打印正确的值。

现在,在Check1返回后,点击Check2标签上的刷新会从Check2中的会话中获取值并显示它。

在此之后,如果我再次刷新两个标签,则在Check1完成(延迟20秒)后viewbag不会返回,尽管Check2为Check2

我的问题是:

  1. 首次打开两个标签页时,由于asyncCheck2Check1会立即返回,但async SessionStateBehavior.Required以后await不会屏蔽Check2返回控制直到等待任务完成。为什么Check1Check2返回之前没有得到值?
  2. 第一次,Check1被卡住,直到Check1返回asyncCheck1,为什么会这样?重新运行应用程序后,{{1}}会立即返回,如前一个问题所述,但只返回一次。
  3. 我的解释中是否有任何遗漏或错误的概念。
  4. 将ASP.NET与.NET Framework 4.5一起使用,在Windows 7 Ultimate上运行Visual Studio 2013。

1 个答案:

答案 0 :(得分:5)

对于你的问题2,如果我理解你正在做的是你刷新两个标签(可能是在check2之前的check1),并观察check2没有完成加载,直到check1结束。

原因是asp.net处理(可能)以串行方式写入同一会话的请求,而不是并行处理。这意味着,只要任何一个请求待处理,第二个请求甚至不会在第一个请求完成之前开始处理。使用任务,异步或手动线程处理将无法解决此问题。原因是访问会话不是线程安全的操作。可能的解决方法是不在不需要它们的页面中使用会话状态,或者在不需要写入会话的页面中使用ReadOnly会话状态。

两者都是通过使用页面上的EnableSessionState属性完成的,可以设置为False以禁用会话状态,也可以设置为ReadOnly以指示您不写入会话对此页面的任何请求。

另请参阅答案here

对于问题1,可能是因为会话状态仅在ReleaseRequestState生命周期事件(在请求的最后执行)之后持久存在,即使我本来期望这样仅在使用非inproc会话存储时才有意义。然而,更可能的原因是check2的请求首先执行(并且甚至阻止check1的处理开始)。也许你可以详细说明你是如何测试它以及如何确定首先执行哪个请求。

修改:

可能发生的事情是:在第一次尝试时,您实际上并未使用相同的会话(当您启动时尚未创建会话并为每个请求创建一个新会话。这也解释了为什么第二个请求在第一次完成之前不会被阻止)。然后,在第二次尝试时,两者都将使用相同的会话(因为两个选项卡都使用了最后写入的会话cookie)。您可以通过打印asp会话(Session.SessionID)id和/或使用无Cookie会话状态来确认该理论