首页 > 编程语言 >c#: AsyncLocal的使用,对比ThreadLocal

c#: AsyncLocal的使用,对比ThreadLocal

时间:2023-04-07 11:33:15浏览次数:52  
标签:Console c# ctx AsyncLocal ScopeContext ThreadLocal WriteLine Id public

一、先说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

相关文章

  • LightOJ - 1041 Road Construction(最小生成树)
    题目大意:给你N条边,看能否形成最小生成树,如果存在,输出值,不存在,另外输出解题思路:模版题#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<map>#include<string>#include<iostream>usingnamespacestd;constintMAXNOD......
  • Linux启动tomcat,应用正常访问
    1.下载tomcat2.安装tomcat3.将生成的war包,放到tomcat的webapps下面4.启动tomcat5.查看应用是否部署成功访问http://ip:端口/项目名称(如:http://192.168.1.206:8080/SpringApiTest/) 6.jmeter进行接口测试......
  • Centos7 安装配置FTP服务
    1、FTP简介ftp(FileTransferProtocol文件传输协议)是基于TCP/IP协议的应用层协议,用于文件的传输,包括ftp服务器(或服务端)和ftp客户端2、关闭防火墙设置selinuxsystemcltstopfirewalld.service&&systemcltdisablefirewalld.serviceSELINUX=disabledsetenforce0使修改立......
  • 昆仑通态MCGS与台达伺服ASD-B2 通讯控制案例
    昆仑通态MCGS与台达伺服ASD-B2 通讯控制案例功能:通过昆仑通态MCGS触摸屏实现与台达ASDB2通讯控制,全通讯控制台达B2驱动器速度设置,速度选择,速度启动,带扭矩限制速度控制,扭矩限制通讯设置。配件:昆仑通态MCGS触摸屏,台达ASDB2伺服驱动器+伺服电机。CN1接线端子,CN3伺服驱动器通讯......
  • cruise模型,增程汽车仿真模型,串联混动,基于cruise simulink联合仿真
    cruise模型,增程汽车仿真模型,串联混动,基于cruisesimulink联合仿真。实现增程器多点控制策略,及电制动优先的能量回收策略。提供cruise模型、控制策略文件以及策略说明文档,方便您在模型基础上进行扩展。YID:6255640342354950......
  • 动易CMS粘贴图片自动上传到服务器(Java版)
    ​ 自动导入Word图片,或者粘贴Word内容时自动上传所有的图片,并且最终保留Word样式,这应该是Web编辑器里面最基本的一个需求功能了。一般情况下我们将Word内容粘贴到Web编辑器(富文本编辑器)中时,编辑器都无法自动上传图片。需要用户手动一张张上传Word图片。如果只有一张图片还能够接......
  • SpringCloud 多个服务启动放在一个窗口下的设置
    进入.idea文件夹,在workspace.xml文件中加入如下配置即可<componentname="RunDashboard"><optionname="configurationTypes"><set><optionvalue="SpringBootApplicationConfigurationType"/></set&......
  • DevEco Device Tool 3.1 Release新版本发布,新增资源管理器、SFTP、HDC
     DevEcoDeviceTool是面向智能设备开发者提供的一站式集成开发环境,支持代码编辑、编译、烧录和调试、性能监测等功能,支持C/C++语言,以插件的形式部署在VisualStudioCode(简称VSCode)上,支持Windows1064位或Ubuntu18.04-21.10版本。本次为大家带来的是DevEcoDeviceTool3.1......
  • C# opc ua客户端实例源码,带ef6+sqlite
    C#opcua客户端实例源码,带ef6+sqlite。代码有完整的注解,及包括所有的链接库和程序结构思维图。纯学习资料YID:2855638904489888......
  • docker-compose 部署 consul v1.15.2
    server1配置文件{"node_name":"consul-server1","datacenter":"zhongtai","domain":"consul","server":true,"log_level":"INFO","ui_conf......