清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

这篇具有很好参考价值的文章主要介绍了清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言:

  自从使用了 AsyncLocal 后,就发现 AsyncLocal 变量像个臭虫一样,在有 AsyncLocal 变量的线程中启动的 Task 、或者 Thread 都会附带 AsyncLocal 变量清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

  在项目使用 AsyncLocal 实现了全局、局部 工作单元 ,但是就无法在后续作业中开启多个线程了(需求就是要开启多个线程,俺也没得办法清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递),后续启动的多线程都会带有 AsyncLocal 变量,直接导致报错,例如 DBContext 不是线程安全的错之类的....。

  其实我一直认为在一个Http请求中开启多个线程,不合适,应该把需要执行的任务交给 “后台工作线程” ,或者交给 “后台Job” ,但现实世界中的情况就是很复杂,怎么办?就是要在Http请求中开启多个线程,还能怎么办呢,去解决 ExecutionContext 、AsyncLocal 传递的问题吧。

  “人天天都学到一点东西,而往往所学到的是发现昨日学到的是错的。

 Thread 中的 ExecutionContext 

  创建一个线程,并启动,Thread执行的委托中会取到 “AsyncLocalTest.Lang.Value” 在线程外部设置的值。

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

   为啥Thread会取到外部的 AsyncLocal 变量中的值呢?深入源代码看下,如下图。

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

  好家伙,Thread.Start() 原来线程启动时,就去执行ExecutionContext.Capture()获取了线程执行上下文,即 ExecutionContext

  如下图,可以看到在Thread线程中可以获取到 ExecutionContext ,从ExecutionContext中可以看到存储在上面的 AsyncLocal 变量

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

Task中的ExecutionContext 

   声明Task时,深入源代码查看

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

   Task 会再执行一个内部构造函数

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

   Task 构造函数中,原来还是通过执行 ExecutionContext.Capture() 获取了 ExecutionContext

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

   创建一个Task时,Task就自动获取了“线程执行上下文 即 ExecutionContext”。

阻止ExecutionContext流动

    如何阻止ExecutionContext流动,请查看这篇文章 https://www.cnblogs.com/eventhorizon/p/12240767.html#3executioncontext-%E7%9A%84%E6%B5%81%E5%8A%A8 ,就不再赘述。

实现一个局部干净的ExecutionContext

    1.实现一个 DisposeAction ,不知道怎么称呼,请看代码吧,源代码来只ABP框架,我直接copy过来的。原理,就是Using代码块释放时,执行这个 “Action 委托”。

清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递
    /// <summary>
    /// 源代码来自ABP Vnext框架
    /// </summary>
    public class DisposeAction : IDisposable
    {
        private readonly Action _action;

        public DisposeAction([NotNull] Action action)
        {
            _action = action ?? throw new ArgumentNullException(nameof(action));
        }

        public void Dispose()
        {
            _action();
        }
    }
DisposeAction

    2. 众所周知 ExecutionContext.SuppressFlow() , 阻断 ExecutionContext 流动 。ExecutionContext.RestoreFlow(), 启动 ExecutionContext 流动 。 

    3. 实现局部阻断 ExecutionContext  流动核心代码

清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递
    public class SuppressExecutionContextFlow
    {
        public static IDisposable CleanEnvironment()
        {
            // 阻断 ExecutionContext 流动
            ExecutionContext.SuppressFlow();
            return new DisposeAction(() =>
            {
                if (ExecutionContext.IsFlowSuppressed())
                {
                    ExecutionContext.RestoreFlow();
                }
            });
        }
    }
SuppressExecutionContextFlow.CleanEnvironment

    4.测试代码,随便调试下

清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递
//6.创建一个干净的 ExecutionContext 环境,供使用
var scheduler = new QueuedTaskScheduler(2);
AsyncLocalTest.Lang.Value = "test";
using (SuppressExecutionContextFlow.CleanEnvironment())
{
    Task task11 = new Task(() =>
    {
        var aa = ExecutionContext.Capture();
        Console.WriteLine("task11线程:" + AsyncLocalTest.Lang.Value);
    });
    Thread th = new Thread(() =>
    {
        var aa = ExecutionContext.Capture();
        Console.WriteLine("th线程:" + AsyncLocalTest.Lang.Value);
    });
    th.Start();
    task11.Start(scheduler);
}
Console.WriteLine("主线程:" + AsyncLocalTest.Lang.Value);
Console.Read();
干净的 ExecutionContext 环境

    调试.gif

    清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递

自此 实现一个局部干净的ExecutionContext 完成,我的代码参考 https://github.com/qiqiqiyaya/Learning-Case/tree/main/CleanExecutionContext

 文章来源地址https://www.toymoban.com/news/detail-450907.html

到了这里,关于清除ExecutionContext,阻止 AsyncLocal 在异步流、Thread中传递的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于transmittable-thread-local的请求参数传递,如traceid,版本等

            在系统中对请求链路根据版本信息进行服务调度,保证请求发送的请求链路的不同的版本服务上。         有很多开源组件支持链路追踪,如spring cloud sleuth。但支持场景有限,需要另行开发,故使用阿里的transmittable-thread-local进行实现,如默认的异步线程支持,ro

    2024年02月07日
    浏览(24)
  • [RDMA] 高性能异步的消息传递和RPC :Accelio

    1. Introduce Accelio是一个高性能异步的可靠消息传递和RPC库,能优化硬件加速。 RDMA和TCP / IP传输被实现,并且其他的传输也能被实现,如共享存储器可以利用这个高效和方便的API的优点。Accelio 是 Mellanox 公司的RDMA中间件,用于高性能异步的可靠消息传递和RPC库。 Accelio提供了一

    2024年02月12日
    浏览(29)
  • 【Golang】golang中http请求的context传递到异步任务的坑

    在golang中,context.Context可以用来用来设置截止日期、同步信号,传递请求相关值的结构体。 与 goroutine 有比较密切的关系。 在web程序中,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他的 goroutine去访问后端资源,比如数据库、RPC服务等,它们需要访

    2024年02月08日
    浏览(30)
  • [翻译]ExecutionContext vs SynchronizationContext

    我最近几次被问到关于 ExecutionContext 和 SynchronizationContext 的各种问题,例如它们之间的区别是什么,“传播(Flow)”它们意味着什么,以及它们与 C# 和 Visual Basic 中新的 async/await 的关系。我想我会尝试在这里解决其中的一些问题。 警告:这篇文章深入到 .NET 的一个高

    2023年04月12日
    浏览(22)
  • 【react】记录一次react组件props依赖异步数据(setFieldsValue)后传递form给子组件,再逐层传递给孙子组件引起的未渲染异常

    背景 react祖父组件设置异步数据(setFieldsValue)后传递form给子组件,再逐层传递给孙子组件引起的未渲染异常,孙子组件如果不设置useEffect和useState去监听value的值进行重渲染,会出现获取得到value最新值,但是不进行渲染的异常 解决前后的代码对比 完整代码(异常): 完整

    2024年01月17日
    浏览(35)
  • vue中父组件异步数据通过props方式传递给子组件,子组件接收不到的问题

    问题描述 组件化开发中经常用到父子组件的通信,父传子子传父等数据的操作,如果父组件的数据是发请求从后端获取的异步数据,那么父组件将这个数据传递给子组件的时候,因为是异步数据,就会出现父组件传递过去了,但是子组件mounted钩子初始情况下是接收不到的问

    2023年04月08日
    浏览(28)
  • .NET的AsyncLocal用法指南

    通过 AsyncLocal 我们可以在一个逻辑上下文中维护一份私有数据,该上下文后续代码中都可以访问和修改这份数据,但另一个无关的上下文是无法访问的。 无论是在新创建的 Task 中还是 await 之后,我们都能够访问前面设置的 AsyncLocal 的数据。 输出结果: AsyncLocal 的实际

    2024年02月10日
    浏览(26)
  • 快速了解4种阻止事件冒泡的方法(原生js阻止,vue中使用修饰符阻止)

    前端结构 事件冒泡:clickSonBox事件发生时clickFatherBox事件也被触发了 此时点击子盒子 对话框弹出两次 方法1:使用js阻止事件冒泡 方法2:使用事件修饰符stop 绑定事件时,使用stop修饰符阻止事件冒泡 方法3:使用事件修饰符self 绑定事件时,使用self修饰符 表示只在本元素被点

    2024年02月16日
    浏览(26)
  • Javascript怎样阻止事件传播?

    在 JavaScript 中,可以使用事件对象的方法来阻止事件传播。事件传播指的是当一个元素上触发了一个事件,该事件会在事件流中传播到父元素或祖先元素,从而影响到它们。 事件传播有三个阶段:捕获阶段、目标阶段和冒泡阶段。阻止事件传播的方法取决于你希望在哪个阶段

    2024年02月13日
    浏览(25)
  • Vue中阻止事件冒泡

    vue中阻止时间冒泡: @click.stop : 阻止事件冒泡 @click.prevent : 阻止事件默认行为 @click.self : 事件只作用在元素本身,而不是其子元素。

    2024年02月13日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包