在 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. char对比varchar

    char对比varchar 相同点:char与varchar都是存储字符串的数据类型 不同点:char是固定长度的字符类型,而varchar是可变长度的字符类型,这个一定要注意.另外进行select时 ...

  2. 通过HashSet达到对象集去重的实现(jdk1.8)

    通过HashSet达到对象集去重的实现(jdk1.8) public class Contract { private String contractId; private String contra ...

  3. Java容器-个人整理1

    1.初始化集合时,若能知道知道容量,尽量初始化时确定容量.容器类一般可以自动扩充,但扩充是有性能代价的. 2.Arrays.asList()的底层表示仍然时数组,因此不能进行调整尺寸的操作. 3.Ha ...

  4. Tomcat 500error: Could not initialize class

    Web 项目报错Could not initialize class ,出现500,说明服务器是起来了,可能是这个类的驱动有问题,缺少初始化需要的文件 查到有相关情况: 考虑是jar 包的问题,检查了 ...

  5. easyui combobox 不能选中值的问题

    easyui comboxbox 下拉框加载到数据,但是不能选中.一般情况是重复渲染,页面有同名元素,valueField重复. 这次遇到的具体问题是,第一次刷新页面,可以选中,第二次不能选中.考虑到 ...

  6. javascript之原型链

    JavaScript 中,万物皆对象!(对于编程而言,可以说万物皆对象.) js中的原型链的作用时什么呢? 我自己的理解是,给一个人赋予一些技能, function people(name,age,s ...

  7. 计算机爱好者协会技术贴markdown第三期

    之前都是给大家展示的对文本的基本操作,这一期我们要给大家展示的就是很高大上的东西了哦 看左边是不是超low,右边是不是瞬间高大上了!!! 一个简单的表格是这么创建的: 爱酱     | Value - ...

  8. Django模板继承和引用

    一.模板继承 1.模板继承可以在创建一个基本“骨架”后,被其它子模板继承并覆盖,通过修改基础模板可以修改子模板中的所有框架 2.在模板teacher文件夹下创建基础模板 {% block xxx}与{ ...

  9. Cortext-A7_i.MX 6ULL——多模式DDR控制器(MMDC)

    1.概述 i.MX 6ULL系列芯片的MMDC是一个多模式DDR控制器,支持DDR3/DDR3Lx16和LPDDR2x16的存储类型,MMDC是可配置,高性能,优化的内存控制器. 注:DDR3/DDR ...

  10. java环境下载

    https://repo.huaweicloud.com/java/jdk/8u201-b09/