C# 会话状态异步时出现意外行为
我正在ASP.NET MVC控制器方法的上下文中调查C# 会话状态异步时出现意外行为,c#,asp.net,session,async-await,asp.net-4.5,C#,Asp.net,Session,Async Await,Asp.net 4.5,我正在ASP.NET MVC控制器方法的上下文中调查async和await,并得到一些意外的行为 我有以下控制器类: [SessionState(System.Web.SessionState.SessionStateBehavior.Required)] public class HomeController : Controller { // GET: Home public ActionResult Index() { return View();
async
和await
,并得到一些意外的行为
我有以下控制器类:
[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();
}
}
[会话状态(System.Web.SessionState.SessionStateBehavior.Required)]
公共类HomeController:控制器
{
//到家
公共行动结果索引()
{
返回视图();
}
公共异步任务检查1()
{
会话[“测试”]=“检查”;
System.Diagnostics.Debug.WriteLine(“会话:“+会话[“测试]”);
等待任务。延迟(20000);
返回视图();
}
公共行动结果检查2()
{
var测试=会话[“测试”];
ViewBag.Test=测试;
返回视图();
}
}
以及每个方法Check1和Check2的简单视图:
检查1
@{
ViewBag.Title = "Check1";
}
<h2>View with Delay</h2>
@{
ViewBag.Title=“检查1”;
}
延迟查看
检查2
@{
ViewBag.Title = "Check2";
var check = ViewBag.Test;
}
<h2>Immediate View @check</h2>
@{
ViewBag.Title=“检查2”;
var检查=ViewBag.Test;
}
即时查看@检查
现在,在启动应用程序后,当我打开http://localhost:23131/Home/Check1
在一个选项卡和http://localhost:23131/Home/Check2
在第二个选项卡中,对Check1
的调用在20秒后按预期返回,对Check2
的调用立即返回,但是它没有在会话状态下设置值,我不明白为什么。应立即设置,因为它在延迟之前。但是,输出窗口上会打印正确的值
现在,在Check1返回后,点击Check2
选项卡上的刷新按钮将从viewbag
中的会话中获取值并显示它
在此之后,如果我再次刷新两个选项卡,Check2
在Check1完成后不会返回(延迟20秒),尽管Check2是async
我的问题是:
Check2
由于Check1
是async
而立即返回,并且尽管SessionStateBehavior,它仍不会被阻止。由于wait
语句在等待的任务完成之前返回控制权,因此需要执行此操作。为什么Check2
在Check1
返回之前不获取值
Check2
被卡住,直到Check1
返回,尽管Check1
是async
,这是为什么?重新运行应用程序后,Check1
立即返回上一个问题中所述的值,但只返回一次将ASP.NET与.NET Framework 4.5结合使用,Visual Studio 2013在Windows 7 Ultimate上运行。对于您的问题2,如果我理解正确,您要做的是刷新两个选项卡(可能是在check2之前带有check1的选项卡),并观察check2直到check1完成后才完成加载 这是因为asp.net处理(可能)以串行方式而不是并行方式写入同一会话的请求。这意味着,只要有一个请求处于挂起状态,第二个请求甚至不会开始处理,直到第一个请求完成。使用任务、异步或手动线程处理无法解决此行为。原因是访问会话不是线程安全的操作。潜在的解决方法是在不需要会话状态的页面中不使用会话状态,或者在不需要写入会话的页面中使用只读会话状态 这两种方法都是通过使用页面上的属性来实现的,设置为
False
以禁用会话状态,或设置为ReadOnly
,以指示您没有在对此页面的任何请求中写入会话
另请参见示例答案
对于问题1,其原因可能是会话状态仅在生命周期事件(在请求的最后执行)之后才持久化,尽管我本以为这仅在使用非会话存储时才起作用。然而,更可能的原因是,先执行对check2的请求(甚至会阻止对check1的处理的开始)。也许您可以详细说明如何准确地测试它,以及如何确保先执行哪个请求
编辑:
可能发生的情况是:在第一次尝试中,您实际上并没有使用相同的会话(启动时尚未创建会话,并且为每个请求创建了一个新的会话。这也解释了为什么在第一次请求完成之前不会阻止第二个请求)。然后,在第二次尝试中,两个选项卡都将使用相同的会话(因为两个选项卡都使用最后写入的会话cookie)。您可以通过打印asp会话(session.SessionID)id和/或对问题2使用无Cookie会话状态来确认这一理论,如果我理解正确,您正在做的是刷新两个选项卡(可能是在check2之前带有check1的选项卡),并观察检查2直到检查1完成后才完成加载 这是因为asp.net处理(可能)以串行方式而不是并行方式写入同一会话的请求。这意味着,只要有一个请求处于挂起状态,第二个请求甚至不会开始处理,直到第一个请求完成。使用任务、异步或手动线程处理无法解决此行为。原因是访问会话不是线程安全的操作。潜在的解决方法是在不需要会话状态的页面中不使用会话状态,或者在不需要写入会话的页面中使用只读会话状态 这两种方法都是通过使用页面上的属性来实现的,设置为
False
以禁用会话状态,或设置为ReadOnly
,以指示您没有在对此页面的任何请求中写入会话
另见fo