Is using [ThreadStatic] incompatible with asynchronous code?
We have a fairly large existing code base for various web services built on top of ASP.NET, and this code makes heavy use of HttpContext.Current.User
(wrapped as Client.User
) access , which I confidently use internally [ThreadStatic]
to give you this ambient definition.
I am currently investigating if it is possible that we start using more asynchronous code in the form async/await
, but I am having a hard time finding a way to use [ThreadStatic]
this. It [ThreadStatic]
is actually impossible to eliminate addiction due to its heavy use.
It is my understanding that when the await
code execution is terminated when pressed , the call returns immediately and that to continue execution, when the asynchronous code returns, a continuation is executed. Meanwhile, the original stream can be used for something else, such as processing another request. So far, I understand that.
What I really can't seem to find a definitive answer to is whether HttpContext.Current.User
before and after is guaranteed or not await
.
So basically:
HttpContext.Current.User = new MyPrincipal();
var user = HttpContext.Current.User;
await Task.Delay(30000);
// Meanwhile, while we wait for that lots of other requests are being handled,
// possibly by this thread.
Debug.Assert(object.ReferenceEquals(HttpContext.Current.User, user));
Is success guaranteed Debug.Assert
?
If another request was being processed by the same thread as the waiting one Task.Delay
, would that request set the other HttpContext.Current.User
, so that the previous state is somehow saved and restored when the continuation is called?
What I can imagine is that behind the scenes, the state [ThreadStatic]
is stored as some kind of dictionary in the thread itself, and that when a thread returns to the thread pool after returning to this one await
, that dictionary is maintained somewhere somewhere and back to the thread. when it does the continuation (or in a thread, I'm not sure if it's necessarily the same thread that handles the continuation), perhaps with a reassuring pat on the butt and "go get 'em boy!", but that last part might just be mine fantasy.
Is this somewhat accurate?
UPDATE: I tried to put together a small test that tries to do this. So far this works and the assertion has not failed for any of the hundreds of requests. Can anyone check if the test makes sense?
source to share
async
/ await
are stream-aggregated, which means they have a convention that can work on several different streaming systems.
In general, ThreadStatic
will not work correctly in async
/ await
, except in trivial cases such as UI contexts where it will await
resume on the UI thread. For ASP.NET, ThreadStatic
not compatible with async
.
However, this HttpContext.Current
is a special case. ASP.NET defines a "request context" (represented by an AspNetSynchronizationContext
assigned instance SynchronizationContext.Current
). By default await
, task execution will capture this sync context and use it to resume the method. When the method resumes, it may be on a different thread, but it will have the same request context (including HttpContext.Current
and also other things like culture and security).
So, it HttpContext.Current
persists, but any of your own values are ThreadStatic
missing.
I describe how it await
works SynchronizationContext
in my async
introduction . If you'd like more details, check out my SynchronizationContext
MSDN article (especially the last section on Asynchronous CTP) .
source to share
ThreadStatic has nothing to do with asynchronous code at all. It is often used in this context to improve performance by giving each thread its own copy of a particular object, eliminating contention.
Of course, it must be used with care. And if you have a scenario that requires the same object instance to be used regardless of which thread the code is running on, then ThreadStatic will either not work or it will require careful thread handling to ensure that each thread of execution runs back into thread where it belongs.
In some async / await scripts you are guaranteed that the continuation happens on the same thread as the original. For example, when you are waiting from a GUI thread in a Forms or WPF program. But this is not guaranteed by async / await functions in general.
Ultimately, while you can use ThreadStatic with async / await, you still need to make sure you use it the way it meant: bind a specific value or object to a specific thread. This is great for general purpose objects, where any thread of execution that might be continued doesn't care which object it actually uses. Or where you are sure that the thread of execution stays in the given thread.
But otherwise, no. You don't want to use ThreadStatic in scenarios where at the same time you need a thread of execution to always use the same object, but you cannot guarantee that the thread of execution stays in the same thread. (Sorry if the last statement seems obvious and hellip, I just want to make sure it's clear).
source to share
Assuming it await
was called from the async call stack (async controller / handler), then yes, the assertion is guaranteed to succeed.
ASP.NET SynchronizationContext will handle HttpContext access on backflows (if you are not using ConfigureAwait(false)
). However, this is not the case for streaming data in general, so prefer HttpContext.Items if you have this type of "global request".
source to share