【问题标题】:Session state unexpected behavior with async使用异步的会话状态意外行为
【发布时间】:2014-11-10 03:08:33
【问题描述】:

我正在调查 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>

检查2

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

<h2>Immediate View @check</h2>

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

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

此后,如果我再次刷新两个选项卡,Check2 在 Check1 完成后不会返回(延迟 20 秒),尽管 Check2 是 async

我的问题是:

  1. 第一次打开两个选项卡时,Check2 立即返回,因为Check1async,尽管SessionStateBehavior.Required 没有阻止它,因为await 语句返回控制直到等待的任务完成。为什么Check2 没有在Check1 返回之前获取值?
  2. 第一次之后,Check2 卡住了,直到 Check1 返回,尽管 Check1async,这是为什么呢?重新运行应用程序后,Check1 会立即返回,如上一个问题所述,但仅返回一次。
  3. 我的解释中有没有遗漏或陈述错误的概念。

将 ASP.NET 与 .NET Framework 4.5 结合使用,Visual Studio 2013 在 Windows 7 Ultimate 上运行。

【问题讨论】:

  • 在你的 web.config 中,httpRuntime.targetFramework 的值是多少?
  • httpRuntime targetFramework="4.5"

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


【解决方案1】:

对于您的问题 2,如果我理解正确,您正在做的是刷新两个选项卡(大概是在 check2 之前带有 check1 的那个),并观察到 ​​check2 直到 check1 完成后才完成加载。

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

两者都是通过使用页面上的EnableSessionState 属性来实现的,设置为False 以禁用会话状态,或设置为ReadOnly 以指示您不会在对该页面的任何请求中写入会话。

另请参阅例如答案here

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

编辑:

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

【讨论】:

  • 感谢您的回复,在所有情况下我都在 check2 之前执行了 check1。
  • 在这种情况下,可能发生的情况是:在您的第一次尝试中,您实际上并没有使用相同的会话(当您开始时尚未创建会话,并且为每个会话创建了一个新会话)请求。这也解释了为什么在第一个请求完成之前不会阻止第二个请求)。然后,在第二次尝试时,两者都将使用相同的会话(因为两个选项卡都使用了最后写入的会话 cookie)。您可以通过打印 asp 会话 (Session.SessionID) id 和/或使用无 cookie 会话状态来确认该理论。
  • 是的,已确认,在第一次尝试中,两个会话具有不同的 sessionID,而在第二种情况下,两者都具有相同的 sessionID。在第一个请求完成并设置会话 cookie 后,两个请求使用相同的会话。它与async 完全无关。删除异步具有相同的行为
  • 感谢详细的回答和 cmets。我已接受您的回答,但请在您的回答中添加最后一条评论,因为它是答案,如果有官方文档的链接来解释这一点,它将很有帮助。
猜你喜欢
  • 2023-03-21
  • 2016-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-29
  • 2021-07-20
  • 2012-12-24
  • 2015-05-11
相关资源
最近更新 更多