Clear the ExecutionContext and prevent AsyncLocal from being passed in asynchronous streams and Thread
Foreword:
Since using AsyncLocal, I have found that AsyncLocal variables are like a bug. Tasks or Threads started in a thread with AsyncLocal variables will come with AsyncLocal variables .
In the project, AsyncLocal is used to implement global and local work units, but it is impossible to open multiple threads in subsequent jobs (the requirement is to open multiple threads, and I have no choice ), subsequent started multi-threads will have AsyncLocal variables, directly leading to Report errors, such as DBContext is not thread-safe…
In fact, I have always believed that it is inappropriate to open multiple threads in one HTTP request. The tasks that need to be performed should be given to “Background Worker Thread” or to “Background Job” ”, but the situation in the real world is very complicated, what should we do? What else can we do to open multiple threads in Http requests? Let’s solve the problem of ExecutionContext and AsyncLocal passing.
”People learn something every day, and what they often learn is that what they learned yesterday is wrong.“
ExecutionContext in Thread
Create a thread and start it. The delegate executed by Thread will get the value set outside the thread by “AsyncLocalTest.Lang.Value” .
Why does Thread get the value in the external AsyncLocal variable? Take a deep look at the source code, as shown below.
Good guys, when Thread.Start() originally started the thread, it executed ExecutionContext.Capture() to obtain the thread execution context, that is, ExecutionContext
As shown below, you can see that the ExecutionContext can be obtained in the Thread thread. From the ExecutionContext, you can see the AsyncLocal variable stored above
ExecutionContext in Task
When declaring a Task, dig into the source code to view
Task will execute an internal constructor
In the Task constructor, it turns out that ExecutionContext
is obtained by executing ExecutionContext.Capture()
When creating a Task, the Task automatically obtains the “Thread execution context, ExecutionContext“.
PreventExecutionContext from flowing
How to prevent the flow of ExecutionContext, please check this article https://www.cnblogs.com/eventhorizon/p/12240767.html#3executioncontext-%E7%9A%84%E6%B5 %81%E5%8A%A8, so I won’t go into details.
Implement a partially clean ExecutionContext
1. Implement a DisposeAction. I don’t know how to call it. Please look at the code. The source code comes only from the ABP framework, I copied it directly strong>. The principle is that when the Using code block is released, this “Action delegate” is executed.
////// Source code comes from ABP Vnext frame /// public class DisposeAction : IDisposable{ private readonly span> Action _action; public DisposeAction([ NotNull] Action action) { _action = action ?? throw new ArgumentNullException(nameof(action)); } public void span> Dispose() { _action(); } }
DisposeAction
2. As we all know, ExecutionContext.SuppressFlow() blocks the flow of ExecutionContext. ExecutionContext.RestoreFlow(), starts the ExecutionContext flow.
3. Implement partial blocking of ExecutionContext flowing core code
public class SuppressExecutionContextFlow { public static span> IDisposable CleanEnvironment() { // Block ExecutionContext flow ExecutionContext.SuppressFlow(); return new span> DisposeAction(() => { if (ExecutionContext. IsFlowSuppressed()) { ExecutionContext.RestoreFlow(); } }); } }
SuppressExecutionContextFlow.CleanEnvironment
4. Test the code and debug it as you like
//6. Create A clean ExecutionContext environment for use var scheduler = new QueuedTaskScheduler(2); AsyncLocalTest.Lang.Value = "test"; using (SuppressExecutionContextFlow. CleanEnvironment()) { Task task11 = new Task(() => { var aa = ExecutionContext .Capture(); Console.WriteLine("task11 thread:" + AsyncLocalTest.Lang.Value); }); Thread th = new Thread(() => { var aa = ExecutionContext .Capture(); Console.WriteLine("th thread:" + AsyncLocalTest.Lang.Value); }); th.Start(); task11.Start(scheduler); } Console.WriteLine("Main thread:" + AsyncLocalTest.Lang.Value); Console.Read();
Clean ExecutionContext environment
Debugging.gif
Since then implementing a partially clean ExecutionContext is completed, my code reference https://github.com/qiqiqiyaya/Learning-Case/tree/main/CleanExecutionContext
The author of this article: youliCC
Please indicate the original link when reprinting: https://www.cnblogs.com/youlicc/p/17410530.html
e);
Console.Read();
Clean ExecutionContext environment
Debugging.gif
Since then implementing a partially clean ExecutionContext is completed, my code reference https://github.com/qiqiqiyaya/Learning-Case/tree/main/CleanExecutionContext
The author of this article: youliCC
Please indicate the original link when reprinting: https://www.cnblogs.com/youlicc/p/17410530.html