所以,首先是免责声明 - 我是这个异步编程的新手。所以在我开始实现我当前的项目之前,我已经运行了一系列测试来解决问题并更好地理解整个事情。
今天我遇到了与 HttpContext.Current.Session 相关的问题,这个问题恰好是我当前的代码库。
这是我的小测试应用程序:
public partial class RandomTests : System.Web.UI.Page
{
protected async void Page_Load(object sender, EventArgs e)
{
Debug.WriteLine("----------------------------------------------------------------------------------");
Debug.WriteLine("Blend dual call start");
Debug.WriteLine("Operation complete. Returned: " + await BlendDualCall() + " at " + DateTime.Now.ToString("mm':'ss':'fff"));
Debug.WriteLine("Blend dual call complete");
Debug.WriteLine("----------------------------------------------------------------------------------");
}
public async Task<string> BlendDualCall()
{
var cTask = Task.Run(() => YLogin(1000));
var fTask = Task.Run(() => FLogin(2000));
Debug.WriteLine("Awaiting results.");
var yResult = await cTask;
Debug.WriteLine("YLogin result returned at " + DateTime.Now.ToString("mm':'ss':'fff"));
var fResult = await fTask;
Debug.WriteLine("FLogin result returned at " + DateTime.Now.ToString("mm':'ss':'fff"));
return yResult + " " + fResult;
}
#region Non-Async Methods
public string FLogin(int waitTime)
{
Debug.WriteLine("FLogin process executed on thread id: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(waitTime);
//Session is null here
return HttpContext.Current.Session.SessionID + "_" + Thread.CurrentThread.ManagedThreadId;
}
public string YLogin(int waitTime)
{
Debug.WriteLine("YLogin process executed on thread id: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(waitTime);
//Session is not null here (System.Web.SessionState.HttpSessionState). Whats the difference?
return Session.SessionID + "_" + Thread.CurrentThread.ManagedThreadId;
}
#endregion
}
web.config中的自定义SessionState提供程序:
<sessionState mode="Custom" customProvider="RedisSessionProvider">
<providers>
<add name="RedisSessionProvider" type="Microsoft.Web.Redis.RedisSessionStateProvider" host="******" port="***" accessKey="********" ssl="true" />
</providers>
</sessionState>
我正在寻找一些关于SessionState发生了什么以及为什么它在异步调用中为空的理解。有趣的是,HttpContext.Current.Session.SessionID为null但Session.SessionID不为null,我怀疑它使用的是另一个SessionState提供程序?关于该主题,重要的是要注意我使用RedisSessionProvider作为自定义SessionState提供程序(对于Azure)。我是否需要使用RedisSessionProvider来允许它处理异步调用?有人可以指出我正确的方向,也许可以解释一下SessionState如何处理异步调用?
TIA
答案 0 :(得分:1)
无法同时跨多个线程共享会话。这是所有ASP.NET会话的限制,而不仅仅是Redis会话。
核心问题是使用Task.Run
。 Task.Run
将使ASP.NET在每个请求中使用多个线程,这是一个非常糟糕的主意,因为它会严重影响您的可伸缩性。 Task.Run
也明确地在请求上下文之外执行,因此HttpContext.Current
将为null
- 这是设计使然。
最佳解决方案是删除对Task.Run
的所有来电。您可以很好地执行多个并发异步操作 - 如果它们实际上是异步的(不仅仅是在后台线程上运行)。 E.g:
// True asynchronous calls
public async Task<string> FLogin(int waitTime)
{
await Task.Delay(waitTime);
return HttpContext.Current.Session.SessionID + "_" + Thread.CurrentThread.ManagedThreadId;
}
public async Task<string> BlendDualCall()
{
var cTask = YLogin(1000);
var fTask = FLogin(2000);
...
}
如果它们不是真正的异步,那么下一个最佳解决方案是一次执行一个。 E.g:
public string FLogin(int waitTime);
public string BlendDualCall()
{
var yResult = YLogin(1000);
var fResult = FLogin(2000);
...
}
如果你绝对必须在服务器上执行并行代码(再次,我真的不能推荐这个),那么你需要提取你需要的所有数据在开始并行工作之前的会话。 E.g:
public string FLogin(int waitTime, string sessionId)
{
Thread.Sleep(waitTime);
return sessionID + "_" + Thread.CurrentThread.ManagedThreadId;
}
public string BlendDualCall()
{
var sessionId = HttpContext.Current.Session.SessionID.ToString();
var cTask = Task.Run(() => YLogin(1000, sessionId));
var fTask = Task.Run(() => FLogin(2000, sessionId));
...
}