c# - RedisSessionProvider 和异步方法

标签 c# azure asynchronous redis

所以,首先是免责声明——我是异步编程的新手。因此,在我开始实现我当前的项目之前,我一直在运行一系列测试来解决问题并更好地了解整个事情。

我今天遇到了一个与 HttpContext.Current.Session 为 null 有关的问题,这恰好遍布我当前的代码库。

这是我的小测试应用程序:

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 发生了什么以及为什么它在异步调用中为 null 的理解。有趣的是,HttpContext.Current.Session.SessionID 为空但 Session.SessionID 不为空,我怀疑它使用的是不同的 SessionState 提供程序?关于该主题,重要的是要注意我使用 RedisSessionProvider 作为自定义 SessionState 提供程序(用于 Azure)。我需要对 RedisSessionProvider 做些什么才能让它处理异步调用吗?有人能给我指出正确的方向吗?也许可以简单解释一下 SessionState 如何处理异步调用?

TIA

最佳答案

session 不能同时跨多个线程共享。这是所有 ASP.NET session 的限制,而不仅仅是 Redis session 。

核心问题是Task.Run的使用。 Task.Run 将使 ASP.NET 每个请求使用 多个 线程,这是一个非常糟糕的主意,因为它会严重影响您的可伸缩性。 Task.Run 还明确地跳出请求上下文,因此 HttpContext.Current 将为 null - 这是设计使然。

最好的解决方案是删除对 Task.Run 的所有调用。您可以很好地执行多个并发异步操作 - 如果它们实际上是异步的(不仅仅是在后台线程上运行)。例如:

// 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);
  ...
}

如果它们不是真正的异步,那么下一个最佳解决方案是一次执行一个。例如:

public string FLogin(int waitTime);
public string BlendDualCall()
{
  var yResult = YLogin(1000);
  var fResult = FLogin(2000);
  ...
}

如果您绝对必须在服务器上执行并行代码(同样,我真的不推荐这样做),那么您需要在开始之前从 session 中提取所有需要的数据并行工作。例如:

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));
  ...
}

关于c# - RedisSessionProvider 和异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45495902/

相关文章:

java - 如何链接可完成的 future

c# - 使用 Parallel.Foreach 中的 ParallelOptions 在串行和并行操作之间切换

c# - Visual Studio 2017 RC 中缺少 Reportviewer 工具

azure - 如何在Azure App Service中配置多个虚拟应用程序?

azure - 使用 azure devops 部署到 azure webapp 时维护一些文件夹

javascript - Async/Await with Request-Promise 返回 Undefined

c# - 使用 SemaphoreSlim 限制对共享变量的访问是否可以保证所有写入都可见?

c# - 如何在不创建窗口的情况下将 WPF UserControl 呈现为位图

c# - 没有异步 API 时如何并行化 I/O 操作

c# - 在本地和云上配置 Azure 共享缓存