Am I correct that async/await itself has nothing to do with concurrency/parallelism and is nothing more than continuation-passing style (CPS) implementation? And the real threading is performed by SynchronizationContext
instance that await
passes/restores?
If that is correct I have the following question about SynchronizationContext
:
it guarantees that a continuation will be executed on the same thread.
However, are there any guaranties that the thread's context information is persisted? I mean Name
, CurrentPrincipal
, CurrentCulture
, CurrentUICulture
, etc. Does it depend on framework (ASP.NET, WinForms, WCF, WPF)?
Context is a data type that you can use to store sets of data. Contexts are like the struct data type in C/C++. Contexts can be used to store elements of any combinations of data types, including other contexts and arrays.
When you are reading and come across an unfamiliar word, you can often look at the context to help you figure out at least an approximate definition for that word. The other words in the rest of the sentence or paragraph influence and clarify the unfamiliar word and provide its context.
A context object encapsulates the references/pointers to services and configuration information used/needed by other objects. It allows the objects living within a context to see the outside world.
The context segment identifies typical situations where the problem occurs, as well as the forces and conditions hindering a possible solution.
Am i correct that async/await itself has nothing to do with concurrency/parallelism and is nothing more than CPS implementation?
Well, async
/ await
is a rewriting that uses CPS, so your core understanding is correct.
Regarding "concurrency" and "parallelism", I would say that it does enable concurrency; you can start multiple async
operations which are all "in flight" at the same time. This is easy to do with Task.WhenAll
and Task.WhenAny
.
Also, even though async
by itself doesn't imply "multithreading", Task.Run
does enable easy async
-compatible multithreading
And the real threading is performed by SynchronizationContext instance that await passes/restores?
Think of it this way: the continuation created by the CPS rewriting has to run somewhere. The captured "async context" can be used to schedule the continuation.
Side note: the captured context is actually SynchronizationContext.Current
unless it is null, in which case the captured context is TaskScheduler.Current
.
Another important note: the capturing and restoring of the context is actually up to the "awaiter" object. So, by default, if you await
a Task
(or any other built-in awaitable), the context will be captured and restored. But if you await
the result of ConfigureAwait(false)
, then the context is not captured. Similarly, if you await
your own custom awaitable, it won't capture the context (unless you program it to).
However, are there any guaranties that the thread's context information is persisted? I mean Name, CurrentPrincipal, CurrentCulture, CurrentUICulture, etc.
SynchronizationContext
is different than ExecutionContext
. A simplified answer is that ExecutionContext
always "flows", so CurrentPrincipal
flows (if it didn't, it could be a security issue, which is why APIs that don't flow ExecutionContext
always end in Unsafe
).
In UI apps, culture doesn't flow, but by default it's the same for all threads anyway. Name
is definitely not going to flow, unless you resume on the same thread (e.g., using a UI SynchronizationContext
).
For some further reading, I recommend starting with my own async
/ await
tutorial and then the official async
/ await
FAQ. Then take a look at Stephen Toub's blog post on ExecutionContext
vs. SynchronizationContext
.
You may also find my SynchronizationContext
article helpful.
No, the async
/await
keywords have everything to do with concurrency. async
/await
basically wrap your method code in to a task and continuation. To see the exact translation that the compiler produces (using the Task Parallel Library) disassemble some code snippet. This translation of async
/await
usage is 'similar' (but not identical!) to the example below
async Task<int> TaskOfTResult_MethodAsync() { int hours; // . . . // Return statement specifies an integer result. return hours; } // Calls to TaskOfTResult_MethodAsync Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync(); int intResult = await returnedTaskTResult; // or, in a single statement int intResult = await TaskOfTResult_MethodAsync();
this is approxiamtely converted to
private int Result() { int hours; // . . . // Return statement specifies an integer result. return hours; }
where you wait for the return outside of the method like
int? hours = null; Task<int> task = null; task = Task.Factory.StartNew<int>(() => Result()); task.ContnueWith(cont => { // Some task completion checking... hours = task.Result; }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current);
Or, you could place the TPL code into the Result
method
private int ResultAsync() { int? hours = null; Task<int> task = null; task = Task.Factory.StartNew<int>(() => { int hours; // . . . // Return statement specifies an integer result. return hours; }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Current); try { return task.Result; } catch (AggregateException aggEx) { // Some handler method for the agg exception. aggEx.Handle(HandleException); } }
SynchronizationContext
does not guarantee that continuation will be executed on the same thread for the async
/awate
code. However, you can set the context using TPL code, via the SynchronisationContex
keyword.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With