Why does AsyncLocal<T> return different results when code is refactored slightly?

人盡茶涼 提交于 2020-12-25 14:05:10

问题


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;
    }
}

回答1:


Follow this link AsyncLocal Class on MSDN

AsyncLocal<T> represents ambient data that is local to a given asynchronous control flow, such as an asynchronous method

It means that your code uses different values when it's accesses from another async method such as WrapperAsync and your main thread contains another value

[UPDATE]
Not obvious thing to understand, but here is explanation. Control Flow in Async Programs. This is how your thread is changed when you do not expect this.

This is how Control Flow working with async

public class Program
{
    private static readonly AsyncLocal<string> AsyncLocalContext = new AsyncLocal<string>();

    public static void Main(string[] args)
    {
        AsyncLocalContext.Value = "No surprise";
        WrapperAsync("surprise!");
        Console.WriteLine("Main: " + AsyncLocalContext.Value);
    }

    private static async void WrapperAsync(string text)
    {
        Console.WriteLine("WrapperAsync before: " + AsyncLocalContext.Value);
        AsyncLocalContext.Value = text;
        Console.WriteLine("WrapperAsync after: " + AsyncLocalContext.Value);
    }
}

Output is:

WrapperAsync before: No surprise
WrapperAsync after: surprise!
Main: No surprise

[/UPDATE]




回答2:


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.



来源:https://stackoverflow.com/questions/49232514/why-does-asynclocalt-return-different-results-when-code-is-refactored-slightly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!