1024programmer Asp.Net [Translation] ExecutionContext vs SynchronizationContext

[Translation] ExecutionContext vs SynchronizationContext

[Translation] ExecutionContext vs SynchronizationContext

I’ve been asked a few times recently various questions about ExecutionContext and SynchronizationContext, such as what’s the difference between them, what it means to “Flow” them, and how they relate to the new async in C# and Visual Basic Relation to the /await keyword. I thought I’d try to address some of these issues here.

Warning: This article dives into an advanced area of ​​.NET that most developers will never need to consider.

What is an ExecutionContext and what does it mean to make it “spread”?

ExecutionContext is one of those things that most developers don’t need to think about. It’s a bit like air: It’s important to have it there, but we don’t think about it until certain critical moments, such as when it goes wrong. In fact, ExecutionContext is just a container for other contexts. Some of these contexts are auxiliary and others are very important to .NET’s execution model, but they all follow the same philosophy I described when describing ExecutionContext: if you must know they exist, either you’re doing super advanced things, or something went wrong.

ExecutionContext Information related to “environment” or “context”, which means that it stores data related to the current execution environment or “context”. In many systems, this context information is maintained in thread local storage (TLS), such as in a ThreadStatic field or a ThreadLocal . In a synchronous world, this kind of thread-local information is enough: everything happens on that thread, so no matter which stack frame you’re in on that thread, which function is executing, etc., all code running on that thread can See and be affected by data specific to that thread. For example, one of the contexts contained by the ExecutionContext is the SecurityContext, which maintains information such as the current “principal” and Code Access Security (CAS) denies and permits. Such information can be associated with the current thread, so if a stack frame denies access to a certain permission, and then calls another method, the called method will still be subject to the denied permissions set on the thread: When an operation is performed, the CLR will check the current thread’s deny permission to see if the operation is allowed, and will find the data that the caller put there.

When you move from a synchronous world to an asynchronous world, things get more complicated. Suddenly, thread-local storage (TLS) became rather irrelevant. In the synchronous world, if I perform operations A, B, and C, all three operations execute on the same thread, so all three operations are affected by ambient data stored on that thread. But in an asynchronous world, I might start A on one thread, and finish it on another, such that operation B might start or run on a different thread than A, and similarly, C might start or run on a different thread than B started or run on the thread. This means that the kind of ambient context we used to rely on to control execution details is no longer possible because TLS does not “flow” across these asynchronous points. Thread-local storage is thread-specific, and these asynchronous operations are not bound to a specific thread. However, there is usually a logical control flow, and we want this environment data to be propagated along with the control flow, such that the environment data moves from one thread to another. That’s what ExecutionContext is for.

ExecutionContext is really just a state bag that can be used to capture all the state from one thread and restore it on another thread while the logical control flow continues. The ExecutionContext can be captured using the static Capture method:

// ambient state captured into ec
 ExecutionContext ec = ExecutionContext. Capture();

And resume during invocation of the delegate via the static run method:

ExecutionContext.Run(ec, delegate
 {
     … // code here will see ec’s state as ambient
 }, null);

All methods in the .NET Framework that derive asynchronous work capture and restore the ExecutionContext in this way (except for methods prefixed with “Unsafe”, which are unsafe because they explicitly do not propagate the ExecutionContext). For example, when you use Task.Run, the Run method captures the ExecutionContext from the calling thread and stores the ExecutionContext instance in the Task object. When the delegate provided by Task.Run is later invoked as part of this Task’s execution, the stored context will be used to make the invocation through ExecutionContext.Run. This is true for Task.Run, ThreadPool.QueueUserWorkItem, Delegate.BeginInvoke, Stream.BeginRead, DispatcherSynchronizationContext.Post, and any other asynchronous API you can think of. They both capture the ExecutionContext, store it, and then use the stored context after calling some code’s execution period.

When we talk about “propagating the ExecutionContext”, we are talking about the process of restoring the previous state of the environment on one thread, and restoring that state at some later point in time.Here’s what happens with this code. The user clicks the button1 button, causing the UI framework to call the button1_Click method on the UI thread. The code then starts a work item to run on the thread pool (via Task.Run). The work item starts some download work and asynchronously waits for it to complete. Then another work item on the thread pool does some computationally intensive operation on the result of that download and returns the result, causing the Task waiting on the UI thread to complete. At this point, the UI thread handles the remainder of this button1_Click method and stores the calculation result in the Text property of button1.

My expectations are valid if the SynchronizationContext is not propagated as part of the ExecutionContext. However, I will be very disappointed if it spreads. Task.Run captures the ExecutionContext when called, and uses it to run the delegate passed to it. This means that in the process of calling DownloadAsync and waiting for the result, the UI SynchronizationContext will flow into the Task and be in the current state at the time of the call. The await will then see the current SynchronizationContext and send the rest of the async method as a continuation to run on the UI thread. This means that my Compute method is likely to run on the UI thread, rather than in the thread pool, causing application responsiveness question.

Now the situation is a bit confusing: ExecutionContext actually has two Capture methods, but only one is public. The internal method (internal to mscorlib) is the method used by most of the asynchronous functions exposed from mscorlib, and it optionally allows the caller to capture the SynchronizationContext in the ExecutionContext; correspondingly, there is also an internal overloaded Run method that supports ignoring the stored SynchronizationContext in ExecutionContext, which actually pretends not to be caught (this is the overload used by most functions in mscorlib). This means that any asynchronous operation implementing the core in mscorlib will not propagate the SynchronizationContext as part of the ExecutionContext, but any asynchronous operation implementing the core anywhere else will propagate the SynchronizationContext as part of the ExecutionContext. I mentioned earlier that “generators” for async methods are the types responsible for propagating the ExecutionContext in async methods, those generators do exist in mscorlib, and they do use internal overloading…so SynchronizationContext doesn’t come with await And spread as part of the ExecutionContext (this is different from the way the task waiter supports capturing the SynchronizationContext and Posting back to it). To help deal with situations where the ExecutionContext propagates the SynchronizationContext, the async method infrastructure tries to ignore the SynchronizationContext that is made current due to propagation.

In short, SynchronizationContext.Current does not “propagate” waitpoints.

Original link: https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/

This article is from the internet and does not represent1024programmerPosition, please indicate the source when reprinting:https://www.1024programmer.com/translation-executioncontext-vs-synchronizationcontext/

author: admin

Previous article
Next article

Leave a Reply

Your email address will not be published. Required fields are marked *

Contact Us

Contact us

181-3619-1160

Online consultation: QQ交谈

E-mail: [email protected]

Working hours: Monday to Friday, 9:00-17:30, holidays off

Follow wechat
Scan wechat and follow us

Scan wechat and follow us

Follow Weibo
Back to top
首页
微信
电话
搜索