一、先说ThreadLocal
在以前写代码的时候,还没有异步的概念,那个时候我们处理HTTP请求就只用一个线程就搞定了,有的时候我们想在代码中共享一个对象,我们希望将这个对象绑定到线程上。
如下:
class Program { private static ThreadLocal<WebContext> threadLocal = new ThreadLocal<WebContext>(() => { var ctx = new WebContext(); Console.WriteLine($"创建WebContext"); return ctx; }); static void Main(string[] args) { Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}"); //模拟5个HTTP请求 for (var i = 0; i < 5; i++) { var index = i; Task.Factory.StartNew(() => { var ctx = threadLocal.Value; ctx.Name = "请求" + index; ctx.Id = index; Console.WriteLine($"请求结束:{index} ctx.Name={ctx.Name} ctx.Id={ctx.Id}"); }); } Console.Read(); } } class WebContext { public string Name { get; set; } public int Id { get; set; } }
class Program { private static AsyncLocal<WebContext> asyncLocal = new AsyncLocal<WebContext>(); static void Main(string[] args) { Console.WriteLine($"主线程: {Thread.CurrentThread.ManagedThreadId}"); //模拟5个HTTP请求 for (var i = 0; i < 5; i++) { var index = i; Task.Factory.StartNew(async () => { await ProcessRequest(index); }); } Console.Read(); } private static async Task ProcessRequest(int i) { var ctx = new WebContext() { Name = "请求" + i, Id = i, }; asyncLocal.Value = ctx; await InnerProcess(i); Console.WriteLine($"请求 {i} end ctx.Name={ctx.Name} ctx.Id={ctx.Id}"); } private static async Task InnerProcess(int i) { Thread.Sleep(100); var ctx = asyncLocal.Value; Console.WriteLine($"请求 {i} ctx.Name={ctx.Name} ctx.Id={ctx.Id}"); ctx.Name += ctx.Id; } } class WebContext { public string Name { get; set; } public int Id { get; set; } }
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("父:设定ctx:" + asyncLocal.Value); await Async1(); Console.WriteLine("父:结束时ctx:" + asyncLocal.Value); } //子上下文 public static async Task Async1() { Console.WriteLine(" 子读取到ctx:" + asyncLocal.Value); await Async1_1(); Console.WriteLine(" 经过孙处理后再读取ctx:" + asyncLocal.Value); asyncLocal.Value = new WebContext { Name = "子", Id = 1, }; Console.WriteLine(" 子改动ctx为:" + asyncLocal.Value); await Async1_1(); Console.WriteLine(" 经过孙处理后再读取ctx:" + asyncLocal.Value); } //孙上下文 public static async Task Async1_1() { Console.WriteLine(" 孙读取到ctx:" + asyncLocal.Value); asyncLocal.Value = new WebContext { Name = "孙", Id = 2, }; Console.WriteLine(" 孙改动ctx为:" + asyncLocal.Value); } } class WebContext { public string Name { get; set; } public int Id { get; set; } public override string ToString() { return $"Name={Name},Id={Id}"; } }
/// <summary> /// 基于 <seealso cref="AsyncLocal{T}"/> 实现的手动控制异步上下文<br /> /// 在 异步控制流 上每个 async/await 或 Task.Run() 方法的调用都会产生新的 一个异步代码块, 这些异步代码块形成了一个树状结构. 这个树状结构有以下特点:<br /> /// <list type="bullet"> /// <item>每个节点可以有一个对象(注意: 值类型和引用类型的处理方式本质是相同的);</item> /// <item>子节点可以读取父节点设置的对象,但是不可以改写;</item> /// <item>子节点也可以设置一个对象,设置后,子节点及其孙节点都可以读取到这个对象;</item> /// <item>父节点读取不到子节点设置的对象;</item> /// </list> /// 基于上面的特性,ScopeContext提供了通过方法包裹达到手动控制 异步代码块 对象的目的.<br /><br /> /// 使用示例(以异步代码块为例,同步是一样的效果): <br /> /// <code> /// public static async Task Main(string[] args) /// { /// var ctx = ScopeContext.Current; /// ctx.SetProperty("name", "outer"); /// Console.WriteLine($"outer:{ctx}"); /// await ScopeContext.RunInScopeAsync(async () => /// { /// ctx = ScopeContext.Current; /// ctx.SetProperty("name", "middle"); /// Console.WriteLine($" middle:{ctx}"); /// await ScopeContext.RunInScopeAsync(async () => /// { /// ctx = ScopeContext.Current; /// ctx.SetProperty("name", "inner"); /// Console.WriteLine($" inner:{ctx}"); /// await Task.CompletedTask; /// }); /// ctx = ScopeContext.Current; /// Console.WriteLine($" middle:{ctx}"); /// }); /// ctx = ScopeContext.Current; /// Console.WriteLine($"outer:{ctx}"); /// /// Console.WriteLine("ok"); /// Console.ReadLine(); /// } /// //输出: /// //outer:{"Id":1,"Dic":{"name":"outer"}} /// // middle: { "Id":2,"Dic":{ "name":"middle"} } /// // inner: { "Id":3,"Dic":{ "name":"inner"} } /// // middle: { "Id":2,"Dic":{ "name":"middle"} } /// //outer: { "Id":1,"Dic":{ "name":"outer"} } /// </code> /// </summary> public class ScopeContext { public static int _count = 0; public int _id = 0; /// <summary> /// 递增Id /// </summary> public int Id => _id; private ConcurrentDictionary<object, object> _dic = new ConcurrentDictionary<object, object>(); private static AsyncLocal<ScopeContext> _scopeContext = new AsyncLocal<Sc opeContext>() { Value = new ScopeContext() }; /// <summary> /// 当前异步控制流节点的上下文 /// </summary> public static ScopeContext Current => _scopeContext.Value; public ScopeContext() { _id = Interlocked.Increment(ref _count); } /// <summary> /// 便于调试 /// </summary> /// <returns></returns> public override string ToString() { return new { Id = Id, Dic = _dic }.ToJson(); } /// <summary> /// 在当前异步控制流节点上存数据 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public void SetProperty(object key, object value) { _dic.TryAdd(key, value); } /// <summary> /// 在当前异步控制节点上取数据 /// </summary> /// <param name="key"></param> /// <returns></returns> public object GetProperty(object key) { _dic.TryGetValue(key, out object value); return value; } /// <summary> /// 开启一个新的异步控制流节点(生成一个新的上下文对象) /// </summary> /// <param name="func"></param> public static void RunInScope(Action func) { RunInScope(() => { func(); return 1; }); } /// <summary> /// 开启一个新的异步控制流节点(生成一个新的上下文对象) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="func"></param> /// <returns></returns> public static T RunInScope<T>(Func<T> func) { return Task.Run(() => { _scopeContext.Value = new ScopeContext(); return func(); }).Result; } /// <summary> /// 开启一个新的异步控制流节点(生成一个新的上下文对象) /// </summary> /// <param name="func"></param> /// <returns></returns> public static async Task RunInScopeAsync(Func<Task> func) { await RunInScopeAsync(async () => { await func(); return 1; }); } /// <summary> /// 开启一个新的异步控制流节点(生成一个新的上下文对象) /// </summary> /// <typeparam name="T"></typeparam> /// <param name="func"></param> /// <returns></returns> public static async Task<T> RunInScopeAsync<T>(Func<Task<T>> func) { _scopeContext.Value = new ScopeContext(); return await func(); } }
测试代码如下:
public class TestScope { //同步测试 public static void Main(string[] args) { var ctx = ScopeContext.Current; ctx.SetProperty("name", "outer"); Console.WriteLine($"outer:{ctx}"); ScopeContext.RunInScope(() => { ctx = ScopeContext.Current; ctx.SetProperty("name", "middle"); Console.WriteLine($" middle:{ctx}"); ScopeContext.RunInScope(() => { ctx = ScopeContext.Current; ctx.SetProperty("name", "inner"); Console.WriteLine($" inner:{ctx}"); }); ctx = ScopeContext.Current; Console.WriteLine($" middle:{ctx}"); }); ctx = ScopeContext.Current; Console.WriteLine($"outer:{ctx}"); Console.WriteLine("ok"); Console.ReadLine(); } 异步测试 //public static async Task Main(string[] args) //{ // var ctx = ScopeContext.Current; // ctx.SetProperty("name", "outer"); // Console.WriteLine($"outer:{ctx}"); // await ScopeContext.RunInScopeAsync(async () => // { // ctx = ScopeContext.Current; // ctx.SetProperty("name", "middle"); // Console.WriteLine($" middle:{ctx}"); // await ScopeContext.RunInScopeAsync(async () => // { // ctx = ScopeContext.Current; // ctx.SetProperty("name", "inner"); // Console.WriteLine($" inner:{ctx}"); // await Task.CompletedTask; // }); // ctx = ScopeContext.Current; // Console.WriteLine($" middle:{ctx}"); // }); // ctx = ScopeContext.Current; // Console.WriteLine($"outer:{ctx}"); // Console.WriteLine("ok"); // Console.ReadLine(); //} }
标签:Console,c#,ctx,AsyncLocal,ScopeContext,ThreadLocal,WriteLine,Id,public From: https://www.cnblogs.com/itjeff/p/17295663.html