.Net AsyncLocal介紹

AsyncLocal的基本概念

AsyncLocal是一個在異步環境中存儲和傳遞狀態的類型。它允許你在線程或任務之間共享數據,而不會受到異步上下文切換的影響。

每一個異步的AsyncLocal的數據都是獨立的

  • AsyncLocal主要是用來在同一個異步控制流內共享對象的,如:一個web請求經過多個 async/await 方法調用後(可能切換了多個線程)依然可以共享同一個對象;
  • AsyncLocal存在層級嵌套的特點,不像ThreadLocal一個線程到底,也就是說AsyncLocal是工作在樹形的異步控制流上的;
    class Program
    {
        private static AsyncLocal<WebContext> threadLocal = new AsyncLocal<WebContext>();
        static void Main(string[] args)
        {
            //模擬5個HTTP請求
            for (var i = 0; i < 5; i++)
            {
                var index = i;
                Task.Factory.StartNew(async () =>
                {
                    var ctx = threadLocal.Value = new WebContext();
                    ctx.Name = "請求" + index;
                    ctx.Id = index;
                    Console.WriteLine($"Delay前 線程ID:{Thread.CurrentThread.ManagedThreadId} ctx.Name={ctx.Name} ctx.Id={ctx.Id}");
                    await Task.Delay(new Random().Next(1000, 2000));
                    Console.WriteLine($"Delay後 線程ID:{Thread.CurrentThread.ManagedThreadId} ctx.Name={ctx.Name} ctx.Id={ctx.Id}");
                });
            }
            Console.Read();
        }
    }

    class WebContext
    {
        public string Name { get; set; }
        public int Id { get; set; }
    }

image

AsyncLocal在樹形異步控制流上流動的特點:

  • 每個節點都可以有自己的對象;
  • 當子節點沒有設置對象時,則訪問的是父節點的對象;
  • 當子節點設置了對象時,則訪問自己設置的對象;
  • 父節點無法訪問子節點設置的對象;
    class Program
    {
        private static AsyncLocal<WebContext> asyncLocal = new AsyncLocal<WebContext>();
        static async Task Main(string[] args)
        {
            await Async();
            Console.Read();
        }

        //父上下文
        public static async Task Async()
        {
            asyncLocal.Value = new WebContext
            {
                Id = 0,
                Name = "父"
            };
            Console.WriteLine("父:" + asyncLocal.Value);
            await Async1();
            Console.WriteLine("父:" + asyncLocal.Value);

        }

        //子上下文
        public static async Task Async1()
        {
            Console.WriteLine("子子:" + asyncLocal.Value);
            asyncLocal.Value = new WebContext
            {
                Name = "子",
                Id = 1,
            };
            Console.WriteLine("子子:修改後");
            Console.WriteLine("子子:" + asyncLocal.Value);
        }

 
    }

    class WebContext
    {
        public string Name { get; set; }
        public int Id { get; set; }

        public override string ToString()
        {
            return $"Name={Name},Id={Id}";
        }
    }

image

AsyncLocal的使用場景

  • 傳遞狀態數據:在異步操作中,例如異步方法或任務鏈中,我們可能需要共享某些狀態數據。使用AsyncLocal,我們可以在異步操作之間傳遞這些狀態數據,而不必顯式地傳遞參數。
  • 上下文相關信息:有時候,我們可能需要跨異步方法或任務訪問一些上下文相關的信息,例如用戶身份驗證信息、語言設置等。使用AsyncLocal,我們可以在整個異步調用棧中訪問這些信息,而不必在每個方法中傳遞它們作爲參數。
    //同一個web請求獲取 商戶上下文數據都是一樣的,而且不會影響另外一個web請求
    public class CurrentContext
    {
        /// <summary>
        /// 商戶
        /// </summary>
        private static readonly AsyncLocal<CurrentUser> CurrentUser = new AsyncLocal<CurrentUser>();

        public static void SetCurrentData(CurrentUser currentUser)
        {
            CurrentUser.Value = currentUser;
        }

        public static CurrentUser GetCurrentData()
        {
            return CurrentUser.Value??new CurrentUser();
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章