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

Posted youliCC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清除ExecutionContext,阻止 AsyncLocal 在异步流Thread中传递相关的知识,希望对你有一定的参考价值。

前言:

  自从使用了 AsyncLocal 后,就发现 AsyncLocal 变量像个臭虫一样,在有 AsyncLocal 变量的线程中启动的 Task 、或者 Thread 都会附带 AsyncLocal 变量

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

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

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

 Thread 中的 ExecutionContext 

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

    

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

    

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

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

    

Task中的ExecutionContext 

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

    

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

    

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

    

   创建一个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 委托”。

    /// <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  流动核心代码

    public class SuppressExecutionContextFlow
    
        public static IDisposable CleanEnvironment()
        
            // 阻断 ExecutionContext 流动
            ExecutionContext.SuppressFlow();
            return new DisposeAction(() =>
            
                if (ExecutionContext.IsFlowSuppressed())
                
                    ExecutionContext.RestoreFlow();
                
            );
        
    
SuppressExecutionContextFlow.CleanEnvironment

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

//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 完成,我的代码参考 https://github.com/qiqiqiyaya/Learning-Case/tree/main/CleanExecutionContext

 

阻止 UITextField 安全文本清除现有文本

【中文标题】阻止 UITextField 安全文本清除现有文本【英文标题】:Stop UITextField Secure Text From Clearing Existing Text 【发布时间】:2013-06-04 19:21:15 【问题描述】:

我有一个 UITextField,它是使用

声明的密码字段

passwordField.secureTextEntry = YES;

我的问题是,当我输入一半密码时,单击另一个组件,然后返回密码字段并再次开始输入,iOS 会清除现有文本。

有没有办法防止这种情况发生或将其关闭?

-亨利

【问题讨论】:

【参考方案1】:

执行此操作的最简单方法是使用类似于此的代码为 UITextField 实现委托:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)replacementString 
  if (textField.text) 
      textField.text = [textField.text stringByReplacingCharactersInRange:range withString:replacementString];
   else 
      textField.text = replacementString;
  

  return NO;

【讨论】:

【参考方案2】:

默认情况下,不,您不能对安全文本字段执行此操作。不过你可以解决它。

实现委托方法textField:shouldChangeCharactersInRange:replacementString: 并使用传递的值来决定做什么。

即如果范围是整个字符串并且替换是空字符串,则不允许更改。新文本可能作为新键入的字符提供,您需要检查该方法实际接收的参数。

【讨论】:

只是出于好奇,您是否尝试过此解决方案,因为我刚刚遇到了类似的问题(显示/隐藏密码),而我的问题是当用户切换显示/隐藏按钮并且 textField 再次返回为secureTextEntry 光标位于字符串的末尾,但是如果输入了一个字符,则删除了前一个字符串,并且仅“保存”了新文本,最后我以编程方式将光标重新定位在 textField 的末尾,这样做了把戏。 @danypata 不,我实际上并没有尝试过,所以它可能需要一些调整,因为我不能确定替换将提供一个空字符串或新键入的字符。我会用这个更新答案。我实际上并没有尝试阻止安全文本字段的标准功能。 这个答案有误导性,UITextField被清除时的范围不是整个字符串,范围仍然是一个。【参考方案3】:

当我更改键盘类型以使我的密码组合为 4(digits)+1(alpha) 以停止清除 secureTextEntry 时,我遇到了同样的情况,

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string 
textField.keyboardType = UIKeyboardTypePhonePad;
[textField reloadInputViews];

return YES;

它会重新加载UITextField,但不会重新加载其文本,因此在不清除文本的情况下,它会将键盘从字母更改为数字,反之亦然。您可以将其用于您自己的功能。

【讨论】:

以上是关于清除ExecutionContext,阻止 AsyncLocal 在异步流Thread中传递的主要内容,如果未能解决你的问题,请参考以下文章

调试器正在寻找 executioncontext.cs,如何修复?

asy CLI

[翻译]ExecutionContext vs SynchronizationContext

NestJS:如何在 canActivate 中模拟 ExecutionContext

Azure Function IWebJobsStartup 实现中的 ExecutionContext

ExecutionContext(执行上下文)综述