最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

.NET?Core利用?AsyncLocal?實(shí)現(xiàn)共享變量的代碼詳解

 更新時(shí)間:2022年04月14日 07:52:48   作者:何言何語(yǔ)  
在Web?應(yīng)用程序中,我們經(jīng)常會(huì)遇到這樣的場(chǎng)景,如用戶(hù)信息,租戶(hù)信息本次的請(qǐng)求過(guò)程中都是固定的,我們希望是這種信息在本次請(qǐng)求內(nèi),一次賦值,到處使用。本文就來(lái)探討一下,如何在.NET?Core?下去利用AsyncLocal?實(shí)現(xiàn)全局共享變量

簡(jiǎn)介

我們?nèi)绻枰麄€(gè)程序共享一個(gè)變量,我們僅需將該變量放在某個(gè)靜態(tài)類(lèi)的靜態(tài)變量上即可(不滿(mǎn)足我們的需求,靜態(tài)變量上,整個(gè)程序都是固定值)。我們?cè)赪eb 應(yīng)用程序中,每個(gè)Web 請(qǐng)求服務(wù)器都為其分配了一個(gè)獨(dú)立線(xiàn)程,如何實(shí)現(xiàn)用戶(hù),租戶(hù)等信息隔離在這些獨(dú)立線(xiàn)程中。這就是今天要說(shuō)的線(xiàn)程本地存儲(chǔ)。針對(duì)線(xiàn)程本地存儲(chǔ) .NET 給我們提供了兩個(gè)類(lèi) ThreadLocal 和 AsyncLocal。我們可以通過(guò)查看以下例子清晰的看到兩者的區(qū)別:

[TestClass]
public class TastLocal {
    private static ThreadLocal<string> threadLocal = new ThreadLocal<string>();
    private static AsyncLocal<string> asyncLocal = new AsyncLocal<string>();
    [TestMethod]
    public void Test() {
        threadLocal.Value = "threadLocal";
        asyncLocal.Value = "asyncLocal";
        var threadId = Thread.CurrentThread.ManagedThreadId;
        Task.Factory.StartNew(() => {
            var threadId = Thread.CurrentThread.ManagedThreadId;
            Debug.WriteLine($"StartNew:threadId:{ threadId}; threadLocal:{threadLocal.Value}");
            Debug.WriteLine($"StartNew:threadId:{ threadId}; asyncLocal:{asyncLocal.Value}");
        });
        CurrThread();
    }
    public void CurrThread() {
        var threadId = Thread.CurrentThread.ManagedThreadId;
        Debug.WriteLine($"CurrThread:threadId:{threadId};threadLocal:{threadLocal.Value}");
        Debug.WriteLine($"CurrThread:threadId:{threadId};asyncLocal:{asyncLocal.Value}");
    }
}

輸出結(jié)果:

CurrThread:threadId:4;threadLocal:threadLocal
StartNew:threadId:11; threadLocal:
CurrThread:threadId:4;asyncLocal:asyncLocal
StartNew:threadId:11; asyncLocal:asyncLocal

從上面結(jié)果中可以看出 ThreadLocal 和 AsyncLocal 都能實(shí)現(xiàn)基于線(xiàn)程的本地存儲(chǔ)。但是當(dāng)線(xiàn)程切換后,只有 AsyncLocal 還能夠保留原來(lái)的值。在Web 開(kāi)發(fā)中,我們會(huì)有很多異步場(chǎng)景,在這些場(chǎng)景下,可能會(huì)出現(xiàn)線(xiàn)程的切換。所以我們使用AsyncLocal 去實(shí)現(xiàn)在Web 應(yīng)用程序下的共享變量。

AsyncLocal 解讀

官方文檔

源碼地址

源碼查看:

public sealed class AsyncLocal<T> : IAsyncLocal
{
    private readonly Action<AsyncLocalValueChangedArgs<T>>? m_valueChangedHandler;
    //
    // 無(wú)參構(gòu)造函數(shù)
    //
    public AsyncLocal()
    {
    }
    //
    // 構(gòu)造一個(gè)帶有委托的AsyncLocal<T>,該委托在當(dāng)前值更改時(shí)被調(diào)用
    // 在任何線(xiàn)程上
    //
    public AsyncLocal(Action<AsyncLocalValueChangedArgs<T>>? valueChangedHandler)
    {
        m_valueChangedHandler = valueChangedHandler;
    }
    [MaybeNull]
    public T Value
    {
        get
        {
            object? obj = ExecutionContext.GetLocalValue(this);
            return (obj == null) ? default : (T)obj;
        }
        set => ExecutionContext.SetLocalValue(this, value, m_valueChangedHandler != null);
    }
    void IAsyncLocal.OnValueChanged(object? previousValueObj, object? currentValueObj, bool contextChanged)
    {
        Debug.Assert(m_valueChangedHandler != null);
        T previousValue = previousValueObj == null ? default! : (T)previousValueObj;
        T currentValue = currentValueObj == null ? default! : (T)currentValueObj;
        m_valueChangedHandler(new AsyncLocalValueChangedArgs<T>(previousValue, currentValue, contextChanged));
    }
}
//
// 接口,允許ExecutionContext中的非泛型代碼調(diào)用泛型AsyncLocal<T>類(lèi)型
//
internal interface IAsyncLocal
{
    void OnValueChanged(object? previousValue, object? currentValue, bool contextChanged);
}
public readonly struct AsyncLocalValueChangedArgs<T>
{
    public T? PreviousValue { get; }
    public T? CurrentValue { get; }
    //
    // If the value changed because we changed to a different ExecutionContext, this is true.  If it changed
    // because someone set the Value property, this is false.
    //
    public bool ThreadContextChanged { get; }
    internal AsyncLocalValueChangedArgs(T? previousValue, T? currentValue, bool contextChanged)
    {
        PreviousValue = previousValue!;
        CurrentValue = currentValue!;
        ThreadContextChanged = contextChanged;
    }
}
//
// Interface used to store an IAsyncLocal => object mapping in ExecutionContext.
// Implementations are specialized based on the number of elements in the immutable
// map in order to minimize memory consumption and look-up times.
//
internal interface IAsyncLocalValueMap
{
    bool TryGetValue(IAsyncLocal key, out object? value);
    IAsyncLocalValueMap Set(IAsyncLocal key, object? value, bool treatNullValueAsNonexistent);
}

我們知道在.NET 里面,每個(gè)線(xiàn)程都關(guān)聯(lián)著執(zhí)行上下文。我們可以通 Thread.CurrentThread.ExecutionContext 屬性進(jìn)行訪(fǎng)問(wèn) 或者通過(guò) ExecutionContext.Capture() 獲取。

從上面我們可以看出 AsyncLocal 的 Value 存取是通過(guò) ExecutionContext.GetLocalValue 和GetLocalValue.SetLocalValue 進(jìn)行操作的,我們可以繼續(xù)從 ExecutionContext 里面取出部分代碼查看(源碼地址),為了更深入地理解 AsyncLocal 我們可以查看一下源碼,看看內(nèi)部實(shí)現(xiàn)原理。

internal static readonly ExecutionContext Default = new ExecutionContext();
private static volatile ExecutionContext? s_defaultFlowSuppressed;
private readonly IAsyncLocalValueMap? m_localValues;
private readonly IAsyncLocal[]? m_localChangeNotifications;
private readonly bool m_isFlowSuppressed;
private readonly bool m_isDefault;
private ExecutionContext()
{
    m_isDefault = true;
}
private ExecutionContext(
    IAsyncLocalValueMap localValues,
    IAsyncLocal[]? localChangeNotifications,
    bool isFlowSuppressed)
{
    m_localValues = localValues;
    m_localChangeNotifications = localChangeNotifications;
    m_isFlowSuppressed = isFlowSuppressed;
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    throw new PlatformNotSupportedException();
}
public static ExecutionContext? Capture()
{
    ExecutionContext? executionContext = Thread.CurrentThread._executionContext;
    if (executionContext == null)
    {
        executionContext = Default;
    }
    else if (executionContext.m_isFlowSuppressed)
    {
        executionContext = null;
    }
    return executionContext;
}
internal static object? GetLocalValue(IAsyncLocal local)
{
ExecutionContext? current = Thread.CurrentThread._executionContext;
if (current == null)
{
    return null;
}
Debug.Assert(!current.IsDefault);
Debug.Assert(current.m_localValues != null, "Only the default context should have null, and we shouldn't be here on the default context");
current.m_localValues.TryGetValue(local, out object? value);
return value;
}
internal static void SetLocalValue(IAsyncLocal local, object? newValue, bool needChangeNotifications)
{
ExecutionContext? current = Thread.CurrentThread._executionContext;
object? previousValue = null;
bool hadPreviousValue = false;
if (current != null)
{
    Debug.Assert(!current.IsDefault);
    Debug.Assert(current.m_localValues != null, "Only the default context should have null, and we shouldn't be here on the default context");
    hadPreviousValue = current.m_localValues.TryGetValue(local, out previousValue);
}
if (previousValue == newValue)
{
    return;
}
// Regarding 'treatNullValueAsNonexistent: !needChangeNotifications' below:
// - When change notifications are not necessary for this IAsyncLocal, there is no observable difference between
//   storing a null value and removing the IAsyncLocal from 'm_localValues'
// - When change notifications are necessary for this IAsyncLocal, the IAsyncLocal's absence in 'm_localValues'
//   indicates that this is the first value change for the IAsyncLocal and it needs to be registered for change
//   notifications. So in this case, a null value must be stored in 'm_localValues' to indicate that the IAsyncLocal
//   is already registered for change notifications.
IAsyncLocal[]? newChangeNotifications = null;
IAsyncLocalValueMap newValues;
bool isFlowSuppressed = false;
if (current != null)
{
    Debug.Assert(!current.IsDefault);
    Debug.Assert(current.m_localValues != null, "Only the default context should have null, and we shouldn't be here on the default context");
    isFlowSuppressed = current.m_isFlowSuppressed;
    newValues = current.m_localValues.Set(local, newValue, treatNullValueAsNonexistent: !needChangeNotifications);
    newChangeNotifications = current.m_localChangeNotifications;
}
else
{
    // First AsyncLocal
    newValues = AsyncLocalValueMap.Create(local, newValue, treatNullValueAsNonexistent: !needChangeNotifications);
}
//
// Either copy the change notification array, or create a new one, depending on whether we need to add a new item.
//
if (needChangeNotifications)
{
    if (hadPreviousValue)
    {
        Debug.Assert(newChangeNotifications != null);
        Debug.Assert(Array.IndexOf(newChangeNotifications, local) >= 0);
    }
    else if (newChangeNotifications == null)
    {
        newChangeNotifications = new IAsyncLocal[1] { local };
    }
    else
    {
        int newNotificationIndex = newChangeNotifications.Length;
        Array.Resize(ref newChangeNotifications, newNotificationIndex + 1);
        newChangeNotifications[newNotificationIndex] = local;
    }
}
Thread.CurrentThread._executionContext =
    (!isFlowSuppressed && AsyncLocalValueMap.IsEmpty(newValues)) ?
    null : // No values, return to Default context
    new ExecutionContext(newValues, newChangeNotifications, isFlowSuppressed);
if (needChangeNotifications)
{
    local.OnValueChanged(previousValue, newValue, contextChanged: false);
}
}

從上面可以看出,ExecutionContext.GetLocalValue 和GetLocalValue.SetLocalValue 都是通過(guò)對(duì) m_localValues 字段進(jìn)行操作的。

m_localValues 的類(lèi)型是 IAsyncLocalValueMap ,IAsyncLocalValueMap 的實(shí)現(xiàn) 和 AsyncLocal.cs 在一起,感興趣的可以進(jìn)一步查看 IAsyncLocalValueMap 是如何創(chuàng)建,如何查找的。

可以看到,里面最重要的就是ExecutionContext 的流動(dòng),線(xiàn)程發(fā)生變化時(shí)ExecutionContext 會(huì)在前一個(gè)線(xiàn)程中被默認(rèn)捕獲,流向下一個(gè)線(xiàn)程,它所保存的數(shù)據(jù)也就隨之流動(dòng)。在所有會(huì)發(fā)生線(xiàn)程切換的地方,基礎(chǔ)類(lèi)庫(kù)(BCL) 都為我們封裝好了對(duì)執(zhí)行上下文的捕獲 (如開(kāi)始的例子,可以看到 AsyncLocal 的數(shù)據(jù)不會(huì)隨著線(xiàn)程的切換而丟失),這也是為什么 AsyncLocal 能實(shí)現(xiàn) 線(xiàn)程切換后,還能正常獲取數(shù)據(jù),不丟失。

總結(jié)

AsyncLocal 本身不保存數(shù)據(jù),數(shù)據(jù)保存在 ExecutionContext 實(shí)例。

ExecutionContext 的實(shí)例會(huì)隨著線(xiàn)程切換流向下一線(xiàn)程(也可以禁止流動(dòng)和恢復(fù)流動(dòng)),保證了線(xiàn)程切換時(shí),數(shù)據(jù)能正常訪(fǎng)問(wèn)。

1.在.NET Core 中的使用示例先創(chuàng)建一個(gè)上下文對(duì)象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace NetAsyncLocalExamples.Context
{
    /// <summary>
    /// 請(qǐng)求上下文  租戶(hù)ID
    /// </summary>
    public class RequestContext
    {
        /// <summary>
        /// 獲取請(qǐng)求上下文
        /// </summary>
        public static RequestContext Current => _asyncLocal.Value;
        private readonly static AsyncLocal<RequestContext> _asyncLocal = new AsyncLocal<RequestContext>();
        /// <summary>
        /// 將請(qǐng)求上下文設(shè)置到線(xiàn)程全局區(qū)域
        /// </summary>
        /// <param name="userContext"></param>
        public static IDisposable SetContext(RequestContext userContext)
        {
            _asyncLocal.Value = userContext;
            return new RequestContextDisposable();
        }
        /// <summary>
        /// 清除上下文
        /// </summary>
        public static void ClearContext()
        {
            _asyncLocal.Value = null;
        }
        /// <summary>
        /// 租戶(hù)ID
        /// </summary>
        public string TenantId { get; set; }
    }
}
namespace NetAsyncLocalExamples.Context
{
    /// <summary>
    /// 用于釋放對(duì)象
    /// </summary>
    internal class RequestContextDisposable : IDisposable
    {
        internal RequestContextDisposable() { }
        public void Dispose()
        {
            RequestContext.ClearContext();
        }
    }
}

 

2.創(chuàng)建請(qǐng)求上下文中間件

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using NetAsyncLocalExamples.Context;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace NetAsyncLocalExamples.Middlewares
{
    /// <summary>
    /// 請(qǐng)求上下文
    /// </summary>
    public class RequestContextMiddleware : IMiddleware
    {
        protected readonly IServiceProvider ServiceProvider;
        private readonly ILogger<RequestContextMiddleware> Logger;
        public RequestContextMiddleware(IServiceProvider serviceProvider, ILogger<RequestContextMiddleware> logger)
        {
            ServiceProvider = serviceProvider;
            Logger = logger;
        }
        public virtual async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            var requestContext = new RequestContext();
            using (RequestContext.SetContext(requestContext))
            {
                requestContext.TenantId = $"租戶(hù)ID:{DateTime.Now.ToString("yyyyMMddHHmmsss")}";
                await next(context);
            }
        }
    }
}

3.注冊(cè)中間件

public void ConfigureServices(IServiceCollection services)
{
	services.AddTransient<RequestContextMiddleware>();
	services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
        app.UseHsts();
    }
    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseRouting();
    app.UseAuthorization();
    //增加上下文
    app.UseMiddleware<RequestContextMiddleware>();
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

一次賦值,到處使用

namespace NetAsyncLocalExamples.Pages
{
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;
        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
            _logger.LogInformation($"測(cè)試獲取全局變量1:{RequestContext.Current.TenantId}");
        }
        public void OnGet()
        {
            _logger.LogInformation($"測(cè)試獲取全局變量2:{RequestContext.Current.TenantId}");
        }
    }
}

到此這篇關(guān)于.NET Core利用 AsyncLocal 實(shí)現(xiàn)共享變量的代碼詳解的文章就介紹到這了,更多相關(guān).NET Core共享變量?jī)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤信息

    在 .NET Core 中使用 Diagnostics (Diagnostic Source) 記錄跟蹤信息

    今天給大家講一下在 .NET Core 2 中引入的全新 DiagnosticSource 事件機(jī)制,為什么說(shuō)是全新呢? 在以前的 .NET Framework 有心的同學(xué)應(yīng)該知道也有 Diagnostics,那么新的 .NET Core 中有什么變化呢?跟隨小編一起看看吧
    2021-06-06
  • asp.net中上傳圖片文件實(shí)現(xiàn)防偽圖片水印并寫(xiě)入數(shù)據(jù)庫(kù)

    asp.net中上傳圖片文件實(shí)現(xiàn)防偽圖片水印并寫(xiě)入數(shù)據(jù)庫(kù)

    asp.net上傳圖片文件實(shí)現(xiàn)防偽圖片水印并寫(xiě)入數(shù)據(jù)庫(kù),需要的朋友可以參考下。
    2010-10-10
  • ASP.NET Core使用GraphQL第一章之Hello World

    ASP.NET Core使用GraphQL第一章之Hello World

    這篇文章主要給大家介紹了關(guān)于ASP.NET Core使用GraphQL第一章之Hello World的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • Asp.NET MVC中使用SignalR實(shí)現(xiàn)推送功能

    Asp.NET MVC中使用SignalR實(shí)現(xiàn)推送功能

    這篇文章主要為大家詳細(xì)介紹了Asp.NET MVC 中使用 SignalR 實(shí)現(xiàn)推送功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • URLRewriter最簡(jiǎn)單入門(mén)介紹 URLRewriter相關(guān)資源

    URLRewriter最簡(jiǎn)單入門(mén)介紹 URLRewriter相關(guān)資源

    配置好后,查看日志看到的狀態(tài)都是200,IIS直接認(rèn)為這個(gè)文件是存在的了, 而不是301,或302,這在某些情況下可能會(huì)不適用,比如:搜索引擎優(yōu)化時(shí)目錄或文件調(diào)整。
    2008-07-07
  • ASP.NET過(guò)濾HTML字符串方法總結(jié)

    ASP.NET過(guò)濾HTML字符串方法總結(jié)

    這篇文章主要介紹了ASP.NET過(guò)濾HTML字符串方法總結(jié),需要的朋友可以參考下
    2014-08-08
  • 如何在ASP.Net Core使用分布式緩存的實(shí)現(xiàn)

    如何在ASP.Net Core使用分布式緩存的實(shí)現(xiàn)

    這篇文章主要介紹了如何在ASP.Net Core使用分布式緩存的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • .net core整合log4net的解決方案

    .net core整合log4net的解決方案

    這篇文章主要給大家介紹了關(guān)于.net core整合log4net的解決方案,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用.net core具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • asp.net 水晶報(bào)表隔行換色實(shí)現(xiàn)方法

    asp.net 水晶報(bào)表隔行換色實(shí)現(xiàn)方法

    水晶報(bào)表隔行換色實(shí)現(xiàn)方法,需要的朋友可以參考下。
    2009-11-11
  • ASP.NET?MVC實(shí)現(xiàn)區(qū)域路由

    ASP.NET?MVC實(shí)現(xiàn)區(qū)域路由

    這篇文章介紹了ASP.NET?MVC實(shí)現(xiàn)區(qū)域路由的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03

最新評(píng)論

和林格尔县| 双辽市| 延长县| 洛浦县| 临泽县| 安岳县| 厦门市| 天祝| 沁源县| 锦屏县| 南昌市| 满城县| 深圳市| 惠安县| 墨脱县| 涟水县| 年辖:市辖区| 威宁| 大新县| 吴旗县| 博客| 隆安县| 巴中市| 科技| 大宁县| 上高县| 彭州市| 英吉沙县| 康定县| 乌鲁木齐县| 文成县| 渭源县| 临沂市| 洛川县| 漠河县| 慈利县| 独山县| 三亚市| 衡南县| 高州市| 郯城县|