I know that a Task may continue the execution on another thread, proven by this code.
public async Task Test()
{
var id1 = System.Environment.CurrentManagedThreadId;
await Task.Delay(1000);
var id2 = System.Environment.CurrentManagedThreadId;
Console.Write($"First {id1} then {id2}");
}
I expect the framework to handle memory barrieres, so that id1 is visible when accessed in the last statement.
But what if you are using some kind of framework e.g. NHibernate where the ISession is not thread safe. Frameworks may even check that the thread ID is still the same. How does this mix?
When building website, I'll to use a IOC container with a nested container per request, but when then thread could change within the same request, wouldn't this lead to all kind of problems? ThreadStatic would not work as expected
Not being thread safe usually means don't use it from multiple threads at the same time, rather than don't use it from one thread and then from another thread later.
I don't know about NHibernate specifically, but if it's a problem, consider using EF Core.
In general with async, don't use variables attached to specific threads, like thread local or thread static.
However, local variables, class members, logical call contexts, HttpContext if you're on asp.net etc continue to work. If you do have something that will be lost after an await, you can usually just save it to a local variable first.
The default ConfigureAwait(true) also restores some context on the continuation, but it can be hard to know what is restored and what isn't. Later versions of .net do a better job of this, like restoring the culture so that resources keep working after an await.
There's a good article by Stephen Toub here:
https://blogs.msdn.microsoft.com/pfxteam/2012/06/15/executioncontext-vs-synchronizationcontext/
Related
I have read that asp.net core doesn't have a sync context and as such any async method continuation will be executed in a threadpool context.
Imagine you have a controller's http action async method that awaits some other method. Does that mean that any code bellow the await is expected to run in a different thread than the code until the await? I mean, does that mean that if this method shares any object with other methods like this one, I should be concerned about shared state in a, apparently, multi threaded code inside this very method (like using mutex on accessing shared stated, etc)?
I have read that asp.net core doesn't have a sync context and as such any async method continuation will be executed in a threadpool context.
This is true.
Does that mean that any code bellow the await is expected to run in a different thread than the code until the await?
It might. It will run on any thread pool thread, which may or may not be the same thread that was running the code before the await.
Note: ASP.NET pre-Core had the same behavior. The synchronization context used by pre-Core ASP.NET did not guarantee staying on the same thread. The thread could be any thread pool thread, just like in Core; the context just handled things like ensuring HttpContext.Current refers to the correct object (this static property no longer exists in Core).
I mean, does that mean that if this method shares any object with other methods like this one, I should be concerned about shared state in a, apparently, multi threaded code inside this very method
Asynchronous code is sequential (not "synchronous"), so the standard pattern of "call this method, await the result; call the next method, await the result" does not have to worry about multithreading within the same method. The thread may change throughout the method, but the awaits serialize the code execution.
Now, if the code does something like Task.WhenAll (or Task.WhenAny), then that's a clue that you need to look deeper. In that case, the code is doing concurrency, and each of those tasks may be running multithreaded on different threads. I cover this in the "Beware Implicit Parallelism" section in my blog post on the subject.
like using mutex
Mutex - with its synchronous-only API and thread-affine state - is incorrect to use with any asynchronous code. Again, this was true in ASP.NET pre-Core as well.
Preface: I don't have a good understanding of the underlying implementation of tasks in C#, only their usage. Apologies for anything I butcher below:
I'm unable to find a good answer to the question of "How can I start a task but not await it?" in C#. More specifically, how can I guarantee that the task completes even if the context of the task is finalized/destroyed?
_ = someFunctionAsync(); is satisfactory for launching and forgetting about a task, but what if the parent is transient? What if the task cannot complete before the parent task? This is a frequent occurrence in controller methods, and tasks written in the fashion _ = someFunctionAsync(); are subject to cancellation.
Example Code:
[HttpGet]
public IActionResult Get()
{
_ = DoSomethingAsync();
return StatusCode(204);
}
In order to combat this cancellation, I created a (fairly stupid, imo) static class to hold onto the tasks so that they have time to complete, but it does not work, as the tasks are cancelled when the parent controller is destroyed:
public static class IncompleteTaskManager
{
private static ConcurrentBag<Task> _incompleteTasks = new();
private static event EventHandler<Task>? _onTaskCompleted;
public static void AddTask(Task t)
{
_onTaskCompleted += (sender, task) =>
{
_incompleteTasks = new ConcurrentBag<Task>(_incompleteTasks.Where(task => task != t));
};
_incompleteTasks.Add(CreateTaskWithRemovalEvent(t));
}
private static async Task CreateTaskWithRemovalEvent(Task t)
{
await t;
_onTaskCompleted?.Invoke(null, t);
}
}
Plus, this seems convoluted and feels like a bad solution to a simple problem. So, how the heck do I handle this? What is the proper way of starting a task, forgetting about it, but guaranteeing it runs to completion?
Edit 1, in case anyone suggests it: I've read posts suggesting that _ = Task.Run(async () => await someFunctionAsync()); may serve my needs, but this is not the case either. Though another thread runs the method, its context is lost as well and the task is cancelled, cancelling the child task.
Edit 2: I realize that the controller example is not necessarily the best, as I could simply write the code differently to respond immediately, then wait for the method to complete before disposing of the controller:
[HttpGet]
public async Task Get()
{
base.Response.StatusCode = 204;
await base.Response.CompleteAsync(); //Returns 204 to caller here.
await DoSomethingAsync();
}
There's a lot to unpack here. I'll probably miss a few details, but let me share a few things that should set up a pretty good foundation.
Fundamentally, what it sounds like you're asking about is how to create background tasks in ASP.NET. In .NET 4.x, there was a QueueBackgroundWorkItem method created for this purpose: it gave your task a new cancellation token to use instead of the one provided by the controller action, and it switched you to a different context for the action you provided.
In asp.net core, there are more powerful (but more complicated) IHostedService implementations, including the BackgroundService, but there's nothing quite as simple as QueueBackgroundWorkItem. However, the docs include an example showing how you can use a BackgroundService to basically write your own implementation of the same thing. If you use their code, you should be able to inject an IBackgroundTaskQueue into your controller and call QueueBackgroundWorkItemAsync to enqueue a background task.
Both of these approaches take care of the need to have something await the tasks that get started. You can never truly "guarantee" completion of any given tasks, but they can at least handle the common use cases more gracefully. For example, they let your hosting environment (e.g. IIS) know that something is still running, so it doesn't automatically shut down just because no requests are coming in. And if the hosting environment is being instructed to shut down, it can signal that fact through the cancellation tokens and you can hopefully quickly get your task into a safe state for shutting down rather than being unceremoniously aborted.
They also handle the problem of uncaught exceptions in the background tasks: the exceptions are caught and logged instead of either being silently eaten or completely killing the application.
Neither of these do anything to help you maintain context about things like the current request or user. This is sensible, because the whole point is to allow an action to extend beyond the scope of any given request. So you'll need to write any code you call in these actions to not rely on HttpContext/IHttpContextAccessor or anything stateful like that. Instead, gather what information you need from the context prior to enqueueing the background task, and pass that information along as variables and parameters to downstream code. This is usually good practice anyway, since the HTTP Context is a responsibility that should stay in controller-level code, while most of your business logic should think in terms of business-level models instead. And relying on State is usually best avoided where possible, to create software that's more reliable, testable, etc.
For other types of applications, there are other approaches you'd need to take. Usually it's best to do an internet search for [framework] background tasks where [framework] is the framework you're working in (WPF, e.g.). Different frameworks will have different restrictions. For example, if you write a console app that expects to run without any interaction beyond the command-line arguments, the Task returned from your Main function will probably need to await all the tasks that you start therein. A WPF app, on the other hand, might kick off several background tasks when events like button clicks are invoked, but there are tricks to make sure you do CPU-intensive work on background threads while only interacting with UI elements while on the UI thread.
how can I guarantee that the task completes even if the context of the task is finalized/destroyed?
...
the tasks are cancelled when the parent controller is destroyed
...
Though another thread runs the method, its context is lost as well and the task is cancelled, cancelling the child task.
Your core question is about how to run a task that continues running after the request completes. So there is no way to preserve the request context. Any solution you use must copy any necessary information out of the request context before the request completes.
Plus, this seems convoluted and feels like a bad solution to a simple problem. So, how the heck do I handle this? What is the proper way of starting a task, forgetting about it, but guaranteeing it runs to completion?
That last part is the stickler: "guaranteeing it runs to completion". Discarding tasks, using Task.Run, and using an in-memory collection of in-progress tasks are all incorrect solutions in this case.
The only correct solution is even more convoluted than these relatively simple approaches: you need a basic distributed architecture (explained in detail on my blog). Specifically:
A durable queue (e.g., Azure Queue). This holds the serialized representation of the work to be done - including any values from the request context.
A background processor (e.g., Azure Function). I prefer independent background processors, but it's also possible to use BackgroundService for this.
The durable queue is the key; it's the only way to guarantee the tasks will be executed.
I see people all over the place recommend using ConfigureAwait(false) where you can, and it is a must for library authors, and so on.
But since the continuation of ConfigureAwait(false) can run on any thread from thread pool, then how can you safely protect against multiple threads accessing the same state in your library?
Say you have the following API for your library:
async Task FooAsync()
{
// Do something
//barAsync and saveToFileAsync are private methods.
await barAsync().ConfigureAwait(false);
// counter is a private field
counter++;
await saveToFileAsync().ConfigureAwait(false);
// Do other things
}
If a UI thread keeps calling this FooAsync (e.g. because of user pressing button), wouldn't this code corrupt the value of counter and the file saved? Since multiple threads might be executing?
I find it hard to fanthom using ConfigureAwait(false) without being thread-safe, except for the simplist cases that do not modify state.
Update
I might not have been clear, but in our team, we decided we are going single-threaded. And so, from the answers below, it seems we can't use ConfigureAwait(false) then, since it introduces the possibility of parallelism, which needs to be controlled using locks and so on.
But since the continuation of ConfigureAwait(false) can run on any thread from thread pool, then how can you safely protect against multiple threads accessing the same state in your library?
await does introduce the possibility of reentrancy, but having it actually cause a problem is rare. Asynchronous code by its nature encourages a more functional kind of structure (inputs to a method are its parameters, and outputs are its return values). It's possible to have asynchronous methods have side effects and depend on state, but it's not terribly common.
Note that it is the await that causes accidental reentrancy. ConfigureAwait(false) resumes on the thread pool, but that doesn't cause the issue here.
If a UI thread keeps calling this FooAsync (e.g. because of user pressing button), wouldn't this code corrupt the value of counter and the file saved? Since multiple threads might be executing?
Yes and sort of. Yes, the counter may get an unexpected value, but it's not necessarily because of multiple threads. Consider the same code without ConfigureAwait(false): you still have multiple invocations of that function running, just on a single thread. They're still fighting over the counter and any other shared state. In that case, because of the single thread, counter++ is atomic, but because it's shared, a single invocation of that function may see the value unexpectedly change when resuming from an await.
With ConfigureAwait(false), you do have the additional concern of accidental parallelism (with await you have accidental reentrancy), so if you have non-threadsafe shared state, things can get worse. Reentrancy can cause unexpected states, but parallelism can cause invalid states.
ConfigureAwait is not about thread-safety. It's about avoiding capturing the context.
If you want your code to be thread-safe, then you should implement it to be. This usually involves using some kind of synchronization construct(s), such as for example a lock.
As already pointed out, your FooAsync() is not thread-safe even if you remove the calls to ConfigureAwait(false). Two or more threads can still call it simultaneously, even in a UI application where there is a SynchronizationContext available.
how can you safely protect against multiple threads accessing the same state in your library?
By synchronizing the access to any shared resource. Assuming counter is the only critical section in your code, you could make the method thread-safe using the Interlocked.Increment API:
async Task FooAsync()
{
...
Interlocked.Increment(ref counter);
...
}
This will increment counter and store the new result as an atomic operation.
There are a bunch of other synchronization constructs as well. Which one to use depends on what you are doing basically. Avoid calling ConfigureAwait(false) is not a way to make code thread-safe though.
I'm trying to figure out if ConfigureAwait(false) should be used on top level requests. Reading this post from a somewhat authority of the subject:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
...he recommends something like this:
public async Task<JsonResult> MyControllerAction(...)
{
try
{
var report = await _adapter.GetReportAsync();
return Json(report, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
return Json("myerror", JsonRequestBehavior.AllowGet); // really slow without configure await
}
}
public async Task<TodaysActivityRawSummary> GetReportAsync()
{
var data = await GetData().ConfigureAwait(false);
return data
}
...it says to using ConfigureAwait(false) on every await except the top level call. However when doing this my exception takes several seconds to return to the caller vs. using it and it and having it come back right away.
What is the best practice for MVC controller actions that call async methods? Should I use ConfigureAwait in the controller itself or just in the service calls that use awaits to request data, etc.? If I don't use it on the top level call, waiting several seconds for the exception seems problematic. I don't need the HttpContext and I've seen other posts that said always use ConfigureAwait(false) if you don't need the context.
Update:
I was missing ConfigureAwait(false) somewhere in my chain of calls which was causing the exception to not be returned right away. However the question still remains as posted as to whether or not ConfigureAwait(false) should be used at the top level.
Is it a high traffic website? One possible explanation might be that you're experiencing ThreadPoolstarvation when you are not using ConfigureAwait(false). Without ConfigureAwait(false), the await continuation is queued via AspNetSynchronizationContext.Post, which implementation boils down to this:
Task newTask = _lastScheduledTask.ContinueWith(_ => SafeWrapCallback(action));
_lastScheduledTask = newTask; // the newly-created task is now the last one
Here, ContinueWith is used without TaskContinuationOptions.ExecuteSynchronously (I'd speculate, to make continuations truly asynchronous and reduce a chance for low stack conditions). Thus, it acquires a vacant thread from ThreadPool to execute the continuation on. In theory, it might happen to be the same thread where the antecedent task for await has finished, but most likely it'd be a different thread.
At this point, if ASP.NET thread pool is starving (or has to grow to accommodate a new thread request), you might be experiencing a delay. It's worth mentioned that the thread pool consists of two sub-pools: IOCP threads and worker threads (check this and this for some extra details). Your GetReportAsync operations is likely to complete on an IOCP thread sub-pool, which doesn't seem to be starving. OTOH, the ContinueWith continuation runs on a worker thread sub-pool, which appears to be starving in your case.
This is not going to happen in case ConfigureAwait(false) is used all the way through. In that case, all await continuations will run synchronously on the same threads the corresponding antecedent tasks have ended, be it either IOCP or worker threads.
You can compare the thread usage for both scenarios, with and without ConfigureAwait(false). I'd expect this number to be larger when ConfigureAwait(false) isn't used:
catch (Exception ex)
{
Log("Total number of threads in use={0}",
Process.GetCurrentProcess().Threads.Count);
return Json("myerror", JsonRequestBehavior.AllowGet); // really slow without configure await
}
You can also try increasing the size of the ASP.NET thread pool (for diagnostics purpose, rather than an ultimate solution), to see if the described scenario is indeed the case here:
<configuration>
<system.web>
<applicationPool
maxConcurrentRequestsPerCPU="6000"
maxConcurrentThreadsPerCPU="0"
requestQueueLimit="6000" />
</system.web>
</configuration>
Updated to address the comments:
I realized I was missing a ContinueAwait somewhere in my chain. Now it
works fine when throwing an exception even when the top level doesn't
use ConfigureAwait(false).
This suggests that your code or a 3rd party library in use might be using blocking constructs (Task.Result, Task.Wait, WaitHandle.WaitOne, perhaps with some added timeout logic). Have you looked for those? Try the Task.Run suggestion from the bottom of this update. Besides, I'd still do the thread count diagnostics to rule out thread pool starvation/stuttering.
So are you saying that if I DO use ContinueAwait even at the top level
I lose the whole benefit of the async?
No, I'm not saying that. The whole point of async is to avoid blocking threads while waiting for something, and that goal is achieved regardless of the added value of ContinueAwait(false).
What I'm saying is that not using ConfigureAwait(false) might introduce redundant context switching (what usually means thread switching), which might be a problem in ASP.NET if thread pool is working at its capacity. Nevertheless, a redundant thread switch is still better than a blocked thread, in terms of the server scalability.
In all fairness, using ContinueAwait(false) might also cause redundant context switching, especially if it's used inconsistently across the chain of calls.
That said, ContinueAwait(false) is also often misused as a remedy against deadlocks caused by blocking on asynchronous code. That's why I suggested above to look for those blocking construct across all code base.
However the question still remains as posted as to whether or not
ConfigureAwait(false) should be used at the top level.
I hope Stephen Cleary could elaborate better on this, by here's my thoughts.
There's always some "super-top level" code that invokes your top-level code. E.g., in case of a UI app, it's the framework code which invokes an async void event handler. In case of ASP.NET, it's the asynchronous controller's BeginExecute. It is the responsibility of that super-top level code to make sure that, once your async task has completed, the continuations (if any) run on the correct synchronization context. It is not the responsibility of the code of your task. E.g., there might be no continuations at all, like with a fire-and-forget async void event handler; why would you care to restore the context inside such handler?
Thus, inside your top-level methods, if you don't care about the context for await continuations, do use ConfigureAwait(false) as soon as you can.
Moreover, if you're using a 3rd party library which is known to be context agnostic but still might be using ConfigureAwait(false) inconsistently, you may want to wrap the call with Task.Run or something like WithNoContext. You'd do that to get the chain of the async calls off the context, in advance:
var report = await Task.Run(() =>
_adapter.GetReportAsync()).ConfigureAwait(false);
return Json(report, JsonRequestBehavior.AllowGet);
This would introduce one extra thread switch, but might save you a lot more of those if ConfigureAwait(false) is used inconsistently inside GetReportAsync or any of its child calls. It'd also serve as a workaround for potential deadlocks caused by those blocking constructs inside the call chain (if any).
Note however, in ASP.NET HttpContext.Current is not the only static property which is flowed with AspNetSynchronizationContext. E.g., there's also Thread.CurrentThread.CurrentCulture. Make sure you really don't care about loosing the context.
Updated to address the comment:
For brownie points, maybe you can explain the effects of
ConfigureAwait(false)... What context isn't preserved.. Is it just the
HttpContext or the local variables of the class object, etc.?
All local variables of an async method are preserved across await, as well as the implicit this reference - by design. They actually gets captured into a compiler-generated async state machine structure, so technically they don't reside on the current thread's stack. In a way, it's similar to how a C# delegate captures local variables. In fact, an await continuation callback is itself a delegate passed to ICriticalNotifyCompletion.UnsafeOnCompleted (implemented by the object being awaited; for Task, it's TaskAwaiter; with ConfigureAwait, it's ConfiguredTaskAwaitable).
OTOH, most of the global state (static/TLS variables, static class properties) is not automatically flowed across awaits. What does get flowed depends on a particular synchronization context. In the absence of one (or when ConfigureAwait(false) is used), the only global state preserved with is what gets flowed by ExecutionContext. Microsoft's Stephen Toub has a great post on that: "ExecutionContext vs SynchronizationContext". He mentions SecurityContext and Thread.CurrentPrincipal, which is crucial for security. Other than that, I'm not aware of any officially documented and complete list of global state properties flowed by ExecutionContext.
You could peek into ExecutionContext.Capture source to learn more about what exactly gets flowed, but you shouldn't depend on this specific implementation. Instead, you can always create your own global state flow logic, using something like Stephen Cleary's AsyncLocal (or .NET 4.6 AsyncLocal<T>).
Or, to take it to the extreme, you could also ditch ContinueAwait altogether and create a custom awaiter, e.g. like this ContinueOnScope. That would allow to have precise control over what thread/context to continue on and what state to flow.
However the question still remains as posted as to whether or not ConfigureAwait(false) should be used at the top level.
The rule of thumb for ConfigureAwait(false) is to use it whenever the rest of your method doesn't need the context.
In ASP.NET, the "context" is not actually well-defined anywhere. It does include things like HttpContext.Current, user principal, and user culture.
So, the question really comes down to: "Does Controller.Json require the ASP.NET context?" It's certainly possible that Json doesn't care about the context (since it can write the current response from its own controller members), but OTOH it does do "formatting", which may require the user culture to be resumed.
I don't know whether Json requires the context, but it's not documented one way or the other, and in general I assume that any calls into ASP.NET code may depend on the context. So I would not use ConfigureAwait(false) at the top-level in my controller code, just to be on the safe side.
This question has been triggered by EF Data Context - Async/Await & Multithreading. I've answered that one, but haven't provided any ultimate solution.
The original problem is that there are a lot of useful .NET APIs out there (like Microsoft Entity Framework's DbContext), which provide asynchronous methods designed to be used with await, yet they are documented as not thread-safe. That makes them great for use in desktop UI apps, but not for server-side apps. [EDITED] This might not actually apply to DbContext, here is Microsoft's statement on EF6 thread safety, judge for yourself. [/EDITED]
There are also some established code patterns falling into the same category, like calling a WCF service proxy with OperationContextScope (asked here and here), e.g.:
using (var docClient = CreateDocumentServiceClient())
using (new OperationContextScope(docClient.InnerChannel))
{
return await docClient.GetDocumentAsync(docId);
}
This may fail because OperationContextScope uses thread local storage in its implementation.
The source of the problem is AspNetSynchronizationContext which is used in asynchronous ASP.NET pages to fulfill more HTTP requests with less threads from ASP.NET thread pool. With AspNetSynchronizationContext, an await continuation can be queued on a different thread from the one which initiated the async operation, while the original thread is released to the pool and can be used to serve another HTTP request. This substantially improves the server-side code scalability. The mechanism is described in great details in It's All About the SynchronizationContext, a must-read. So, while there is no concurrent API access involved, a potential thread switch still prevents us from using the aforementioned APIs.
I've been thinking about how to solve this without sacrificing the scalability. Apparently, the only way to have those APIs back is to maintain thread affinity for the scope of the async calls potentially affected by a thread switch.
Let's say we have such thread affinity. Most of those calls are IO-bound anyway (There Is No Thread). While an async task is pending, the thread it's been originated on can be used to serve a continuation of another similar task, which result is already available. Thus, it shouldn't hurt scalability too much. This approach is nothing new, in fact, a similar single-threaded model is successfully used by Node.js. IMO, this is one of those things that make Node.js so popular.
I don't see why this approach could not be used in ASP.NET context. A custom task scheduler (let's call it ThreadAffinityTaskScheduler) might maintain a separate pool of "affinity apartment" threads, to improve scalability even further. Once the task has been queued to one of those "apartment" threads, all await continuations inside the task will be taking place on the very same thread.
Here's how a non-thread-safe API from the linked question might be used with such ThreadAffinityTaskScheduler:
// create a global instance of ThreadAffinityTaskScheduler - per web app
public static class GlobalState
{
public static ThreadAffinityTaskScheduler TaScheduler { get; private set; }
public static GlobalState
{
GlobalState.TaScheduler = new ThreadAffinityTaskScheduler(
numberOfThreads: 10);
}
}
// ...
// run a task which uses non-thread-safe APIs
var result = await GlobalState.TaScheduler.Run(() =>
{
using (var dataContext = new DataContext())
{
var something = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dataContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);
// ...
// transform "something" and "morething" into thread-safe objects and return the result
return data;
}
}, CancellationToken.None);
I went ahead and implemented ThreadAffinityTaskScheduler as a proof of concept, based on the Stephen Toub's excellent StaTaskScheduler. The pool threads maintained by ThreadAffinityTaskScheduler are not STA thread in the classic COM sense, but they do implement thread affinity for await continuations (SingleThreadSynchronizationContext is responsible for that).
So far, I've tested this code as console app and it appears to work as designed. I haven't tested it inside an ASP.NET page yet. I don't have a lot of production ASP.NET development experience, so my questions are:
Does it make sense to use this approach over simple synchronous invocation of non-thread-safe APIs in ASP.NET (the main goal is to avoid sacrificing scalability)?
Is there alternative approaches, besides using synchronous API invocations or avoiding those APis at all?
Has anyone used something similar in ASP.NET MVC or Web API projects and is ready to share his/her experience?
Any advice on how to stress-test and profile this approach with ASP.NET would be
appreciated.
Entity Framework will (should) handle thread jumps across await points just fine; if it doesn't, then that's a bug in EF. OTOH, OperationContextScope is based on TLS and is not await-safe.
1. Synchronous APIs maintain your ASP.NET context; this includes things such as user identity and culture that are often important during processing. Also, a number of ASP.NET APIs assume they are running on an actual ASP.NET context (I don't mean just using HttpContext.Current; I mean actually assuming that SynchronizationContext.Current is an instance of AspNetSynchronizationContext).
2-3. I have used my own single-threaded context nested directly within the ASP.NET context, in attempts to get async MVC child actions working without having to duplicate code. However, not only do you lose the scalability benefits (for that part of the request, at least), you also run into the ASP.NET APIs assuming that they're running on an ASP.NET context.
So, I have never used this approach in production. I just end up using the synchronous APIs when necessary.
You should not intertwine multithreading with asynchrony. The problem with an object not being thread-safe is when a single instance (or static) is accessed by multiple threads at the same time. With async calls the context is possibly accessed from a different thread in the continuation, but never at the same time (when not shared across multiple requests, but that isn't good in the first place).