https://msdn.microsoft.com/en-us/magazine/gg598924.aspx
It's a great article, and I'm aware that all details could not be covered because that would essentially involve pasting the source code of .NET framework. So quoting the text:
Each thread has a current context. If "Current" is null, then the thread's current context is "new SynchronizationContext()", by convention.
On the other hand, however:
By default, the current SynchronizationContext is captured at an await point, and this SynchronizationContext is used to resume after the await (more precisely, it captures the current SynchronizationContext unless it is null, in which case it captures the current TaskScheduler)
These two statements pretty much contradict each other, so I'm thinking this is the result of some simplification that has been made by the author (I'm fine with it).
Could anyone explain this? Code that might help in answering my question (look for syncCtx
variable), this piece of code is related to the second quote.
The relevant piece of code you're looking for is inside the internal method Task.SetContinuationForAwait
:
// First try getting the current synchronization context.
// If the current context is really just the base SynchronizationContext type,
// which is intended to be equivalent to not having a current SynchronizationContext at all,
// then ignore it. This helps with performance by avoiding unnecessary posts and queueing
// of work items, but more so it ensures that if code happens to publish the default context
// as current, it won't prevent usage of a current task scheduler if there is one.
var syncCtx = SynchronizationContext.CurrentNoFlow;
if (syncCtx != null && syncCtx.GetType() != typeof(SynchronizationContext))
{
tc = new SynchronizationContextAwaitTaskContinuation(
syncCtx, continuationAction, flowExecutionContext, ref stackMark);
}
else
{
// If there was no SynchronizationContext, then try for the current scheduler.
// We only care about it if it's not the default.
var scheduler = TaskScheduler.InternalCurrent;
if (scheduler != null && scheduler != TaskScheduler.Default)
{
tc = new TaskSchedulerAwaitTaskContinuation(
scheduler, continuationAction, flowExecutionContext, ref stackMark);
}
}
It actually does two checks, first to see that it isn't null
, and the second to make sure it isn't the default SynchronizationContext
, which I think is the key point here.
If you open up a Console Application and try to fetch the SynchronizationContext.Current
, you'll definitely see that it can be null
.
class Program
{
public static void Main(string[] args)
{
Console.WriteLine(SynchronizationContext.Current == null ? "NoContext" :
"Context!");
}
}
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