在 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. python数据结构(二)------列表

    本文将重点梳理列表及列表操作. 2.1 list函数 2.2 基本列表操作 2.3 列表方法 2.1 list函数 >>>list('hello') ['h','e','l',l', ...

  2. Spring-boot集成RabbitMQ踩过的坑

    1.java.net.SocketException: socket closed 官方文档已经说明,新建user和guest的账户是没有远程登录的权限的 需要对登录所用账户授权 解决方法: rabb ...

  3. hashtable and hashmap

    hashmap的bucket 和 ‘负载因子’的介绍 https://blog.csdn.net/wenyiqingnianiii/article/details/52204136

  4. 用STS构建spring boot

    操作步骤:1. 登录地址http://spring.io/tools 下载sts,spring-tool-suite-3.9.5.RELEASE-e4.8.0-win32-x86_64.zip2. 解 ...

  5. java -version显示版本和JAvA_HOME配置不一样

    当你需要安装多个版本的jdk时,可能会遇到更改了JAVA_HOME后java -version不变的情况. 一般情况下,将你的JAVA_HOME改为你要用的jdk的安装路径,然后你使用的就是这个版本的 ...

  6. Ado.Net 注意事项

    Ado.Net同一个connection创建sqlCommand时,如果command要执行多个sql及其 params,这是如果某2个params中存在重名的话会报错, 例如sql1和params1 ...

  7. 【转】RPC介绍

    转自:http://www.cnblogs.com/Vincentlu/p/4185299.html 摘要: RPC——Remote Procedure Call Protocol,这是广义上的解释, ...

  8. uiautomatorviewer 优化定位符生成,支持生成Java,Python自动化代码

    项目介绍 二次开发 uiautomatorviewer 优化定位符生成,支持生成Java,Python自动化代码,修复自带工具画面有动态加载时截图失败问题,优化自带工具截图速度 ,实现类似录制脚本功能 ...

  9. ubuntu16 mysql在线安装

    输入"sudo apt-get update"-->回车-->"输入root用户的密码"-->回车: 输入"sudo apt-get ...

  10. 画PCB之电流与线宽的关系

    来源:(多图) 超强整理!PCB设计之电流与线宽的关系http://www.51hei.com/bbs/dpj-39134-1.html 关于PCB线宽和电流的经验公式,关系表和软件网上都很多,本文把 ...