When I call WrapperAsync
AsyncLocalContext.Value
returns null. When I run the same code block outside the method, in the Main
method, AsyncLocalContext.Value
is not null (which is what I would expect).
The functionality is exactly the same yet the results are different. Is this a bug with the Asynclocal
class or is there another explanation?
internal class Program
{
private static readonly AsyncLocal<string> AsyncLocalContext = new AsyncLocal<string>();
private static void Main()
{
const string text = "surprise!";
WrapperAsync(text).Wait();
Console.WriteLine("Get is null: " + (AsyncLocalContext.Value == null));
// AsyncLocalContext.Value is null
var value = GetValueAsync(text).Result;
AsyncLocalContext.Value = value;
Console.WriteLine("Get is null: " + (AsyncLocalContext.Value == null));
// AsyncLocalContext.Value is not null
Console.Read();
}
private static async Task WrapperAsync(string text)
{
var value = await GetValueAsync(text);
AsyncLocalContext.Value = value;
}
private static async Task<string> GetValueAsync(string text)
{
await Task.Delay(0);
return text;
}
}
AsyncLocal<T>
is ambient data stored on the ExecutionContext
of the current thread. ExecutionContext
is flowed across threads automagically in async/await call chains (see Stephen Toub's blog for details). When the app starts, the default ExecutionContext
is used, but once data is stored via AsyncLocal<T>.Value
, a new ExecutionContext
is created for the current async call chain (see here) and the ambient data is added to it. This new context is propagated to downstream calls.
Stephen Cleary discusses this behavior here (scroll down to the AsyncLocal section) and makes the point:
[AsyncLocal] provides a way for contextual information to flow “down” asynchronous calls. Note that the value does not flow “up”.
This is why AsyncLocal<T>
updates down the call chain are not reflected in upstream methods.
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