following the previous post about .NET 4.5 new Async API
this post will focus on AsyncLocal API
the subject of this post brought to my awareness while speaking with
AsyncLocal<T> solving the problem of maintaining call context
for logical call rather than the thread context.
while working with async / await or asynchronous API in general
keeping the call context may become a problem.
the thread context may not represent the logical call context.
putting data on the thread local storage may be bad idea while working
with thread pool (either directly or indirectly via Task).
the following scenario demonstrate the issue:
because the client is not thread safe the Invoker has to get the same client which
construct the same logical call (or have to pass the client via the call chain)
using TPL (.NET 4) this can be solve by:
pay attention to lines 5 and 17.
unfortunately this won’t work with async method like:
on line 3 and line 4 may not execute on the same thread, therefore LocalThread<T> is not helping anymore.
,NET 4.6 target this problem with the AsyncLocal<T> API.
this API maintain context value per logical call context,
this context flow through await boundaries (also when you open new Task or Thread within the logical context)
AsyncLocal<T>solve the issue by changing
anything else stay.
if you cannot afford moving to .NET 4.6 you can still achieve logical call using the old CallContext API
in this case the solution will look as follow:
check lines 5 and 18.
AsyncLocal<T> is quite cool API but there is one thing I didn’t mention.
if you really having non thread safe operation you should be aware that
multiple thread may execution simultaneously on the same logical call context.
like the case of the following snippet:
in this case you should protect the usage of the client.
check the following code snippet:
remember that it is very narrow lock because it’s locking the local context object.
furthermore the .NET lock optimization will likely spin lock on the object and not escalate into kernel mode.