在 DotNetCore 当中不再像 MVC5 那样可以通过 HttpContext.Current 来获取到当前请求的上下文。

不过微软提供了一个 IHttpContextAccessor 来让我们访问当前请求的 Http 上下文,其定义

如下:

namespace Microsoft.AspNetCore.Http
{
public interface IHttpContextAccessor
{
HttpContext HttpContext { get; set; }
}
}

需要使用的话需要将其添加到 Ioc 容器当中,在 Startup 类的 ConfigureService 我们可以将其默认实现注册到 Ioc 之中。

public void ConfigureService(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

那么我们可以来看看 HttpContextAccessor 的具体实现:

using System.Threading;

namespace Microsoft.AspNetCore.Http
{
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); public HttpContext HttpContext
{
get
{
return _httpContextCurrent.Value;
}
set
{
_httpContextCurrent.Value = value;
}
}
}
}

在其内部主要是用了一个 AsyncLocal<HttpContext> 来保存一个 HttpContext 实例,那么 Accessor 是什么时候被赋值的呢?答案就是在每次 HTTP 请求的时候会将其赋值。

AsyncLocal<T> 是什么东西?

AsyncLocal<T> 是在 .Net 4.6 之后推出的一个对象,该对象接受一个泛型参数,其主要作用是保存异步等待上下文中共享某个变量的值。

而异步方法是基于 Task 的自动线程调度,在异步上下文切换的时候可能导致数据丢失。例如在 await 调用之前对某个变量进行了赋值,而这个变量是多个线程间共享的,当 await 调用返回之前的调用点的时候,可能调用点之后的代码还处在之前的线程上,也有可能被调度到其他线程上。

举个例子:

static async Task TestMethod()
{
Console.WriteLine($"当前线程ID{Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(100);
Console.WriteLine($"当前线程ID{Thread.CurrentThread.ManagedThreadId}");
}

在 await 等待任务执行完成之后,后面的代码输出的 ID 与调用之前的 ID 不一样,说明发生了线程切换:

static void Main(string[] args)
{
Action @delegate = async () => await TestMethod(); @delegate();
Console.ReadKey();
}



从代码上看他们似乎在同一个线程,但是在执行的时候就已经发生了线程切换的操作了。

而我们在这里如果使用一个 ThreadLocal<T>变量来存储的话,会发生什么事情呢?

static ThreadLocal<int> _threadLocal = new ThreadLocal<int>();
static AsyncLocal<int> _asyncLocal = new AsyncLocal<int>(); static void Main(string[] args)
{
Action @delegate = async () => await TestMethod(); @delegate();
Console.ReadKey();
} static async Task TestMethod()
{
_threadLocal.Value = 1000;
_asyncLocal.Value = 2000;
Console.WriteLine($"当前线程ID{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"{nameof(_threadLocal)},值:{_threadLocal.Value}");
Console.WriteLine($"{nameof(_asyncLocal)},值:{_asyncLocal.Value}");
await Task.Delay(100);
Console.WriteLine($"当前线程ID{Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"{nameof(_threadLocal)},值:{_threadLocal.Value}");
Console.WriteLine($"{nameof(_asyncLocal)},值:{_asyncLocal.Value}");
}



SO,在这里解释一下,ThreadLocal 是用于为不同的线程保存不同的变量值的,即同一个变量在不同线程当中存储的值可以不一样。在这里使用是为了保证在 TestMethod 方法中变量的唯一性,这个在同步方法用是没问题的,但这里使用了 await 关键字导致等待异步调用结束后代码已经被调度到其他的线程了,所以这里没用。而 AsyncLocal<T> 正是为了这种情况而准备的。

.NET Core 获取 HttpContext.Current 以及 AsyncLocal 与 ThreadLocal的更多相关文章

  1. [C#].Net Core 获取 HttpContext.Current 以及 AsyncLocal 与 ThreadLocal

    在 DotNetCore 当中不再像 MVC5 那样可以通过 HttpContext.Current 来获取到当前请求的上下文. 不过微软提供了一个 IHttpContextAccessor 来让我们 ...

  2. asp.net core获取HttpContext相关操作

    建立类: using System;using System.Collections.Generic;using System.Linq;using System.Threading.Tasks;us ...

  3. 在ASP.NET Core中怎么使用HttpContext.Current

    一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目 ...

  4. NET Core中怎么使用HttpContext.Current

    NET Core中怎么使用HttpContext.Current 阅读目录 一.前言 二.IHttpContextAccessor 三.HttpContextAccessor 回到目录 一.前言 我们 ...

  5. 在ASP.NET Core中怎么使用HttpContext.Current (转载)

    一.前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构.如果我们想使用以前版本中的HttpContext.Current的话,目 ...

  6. 为什么获取的System.Web.HttpContext.Current值为null,HttpContext对象为null时如何获取程序(站点)的根目录

    ASP.NET提供了静态属性System.Web.HttpContext.Current,因此获取HttpContext对象就非常方便了.也正是因为这个原因,所以我们经常能见到直接访问System.W ...

  7. 在.net Core 中像以前那样的使用HttpContext.Current

    今晚在学习.net Core 的使用 拿来以前项目进行改造最简单的问题就是怎么做到让httpcontext 和以前兼容 ,折腾的很久 终于搞定,纪录一下 .net core中使用了无处不在的注入,看了 ...

  8. System.Web.HttpContext.Current.Session获取值出错

    在自定义类库CS文件里使用System.Web.HttpContext.Current.Session获取Session时提示错误:未将对象引用设置到对象的实例. 一般情况下通过这种方式获取Sessi ...

  9. asp.net中处理程序调用HttpContext.Current.Session获取值出错

    asp.net中处理程序调用System.Web.HttpContext.Current.Session获取Session时提示错误:未将对象引用设置到对象的实例. 解决办法:在处理程序文件类中实现I ...

随机推荐

  1. redis多实例

    1.首先在发布系统: 2.安装多实例利用cmd命令安装,切换目录到redis下 (1)首先拷贝一个redis的conf文件(如redis_6380.conf),并且修改里面的服务端口号.日志端口号,以 ...

  2. 在IIS上发布并运行ASP.NET Core

    英文原文地址:https://weblog.west-wind.com/posts/2016/Jun/06/Publishing-and-Running-ASPNET-Core-Application ...

  3. int main(int argc, char** argv) 以及CommandLineParser

    参考链接: 关于int main(int argc, char** argv) http://blog.csdn.net/liuhuiyi/article/details/8239303 http:/ ...

  4. maven阿里云仓库配置

    为了加速项目构建,在maven的settings.xml 文件里配置mirrors的子节点,添加如下mirror: <mirrors> <mirror> <id>a ...

  5. (27)How to stay calm when you know you'll be stressed

    https://www.ted.com/talks/daniel_levitin_how_to_stay_calm_when_you_know_you_ll_be_stressed00:12A few ...

  6. MyBatis-Plus 3.0.3 Sql注入器添加,即全局配置Sql注入器,sqlInjector改写

    官网上写着 但是,这个其实是2.0系列的写法,由于引用了最新的3.0.3这个功能基本不好使. 3.0.3版本的写法 也就是中间加了一层,原来是AutoSqlInjector,现在改为AbstractS ...

  7. Kinfu配置指南

    Kinfu配置指南 欢迎加入Kinfu讨论群:563741937 写给准备配置pcl-kinfu的同学,这个是我的配置方法,已经能用的请无视. 我的配置:Windows7,VS2010(用这个的是不是 ...

  8. MySQL数据库(一)

    一:MySQL的简单介绍 MySQL属于关系型数据库,数据是以行和列的形式去存储(表),表中的每一行叫一条记录,表中的每一列叫一个字段,表和表之间的逻辑关联叫关系. 二:MySQL的基本操作 (一)M ...

  9. [solution]JZOJ-5838 旅游路线

    [solution] JZOJ-5838 旅游路线 Time Limits 1000ms,Memory Limits 128MB 题面 Description GZOI队员们到X镇游玩.X镇是一个很特 ...

  10. TestNG(一)

    1.环境搭建 2.框架结构 3.数据驱动 4.监听器 5.重试逻辑 6.异常测试 7.并发测试