AsyncLocal Value updated to null on ThreadContextChanged

元气小坏坏 提交于 2020-01-13 07:53:07

问题


I'm trying to understand how AsyncLocal should work in .Net 4.6. I'm putting some data into AsyncLocal...but when the ThreadContext changes it is getting set to null. The whole reason I'm using AsyncLocal is to try to preserve/cache this value across threads as I await async operations. Any idea why this would be specifically called and set to a null as the context changes? Documentation on AsyncLocal is very sparse...perhaps I've got it all wrong.

public class RequestContextProvider : IRequestContextProvider
{
    private static readonly AsyncLocal<IRequestContext> _requestContext = new AsyncLocal<IRequestContext>(ValueChangedHandler);

    private static void ValueChangedHandler(AsyncLocalValueChangedArgs<IRequestContext> asyncLocalValueChangedArgs)
    {
        **//This is just here to observe changes...when I await a call that 
        //causes control to pass to a different thread..this is called 
        //with a current value of null.**
        var previousValue = asyncLocalValueChangedArgs.PreviousValue;
        var currentValue = asyncLocalValueChangedArgs.CurrentValue;
        var contextChanged = asyncLocalValueChangedArgs.ThreadContextChanged;
    }

    public void SetContext(IRequestContext requestContext)
    {
        _requestContext.Value = requestContext;
    }

    public IRequestContext GetContext()
    {
        return _requestContext.Value;
    }

}

Here is the example of how it is being called. This is an asynchronous event subscriber that is being called into using an EventStore connection (GetEventStore.com). If the two awaited tasks in here don't do anything (If there are no ids to look up) I have no issues because presumably the task runs synchronously. But if I do have work to do on these tasks I lose my context.

 private async Task PublishProduct(Guid productId, Guid productReferenceId, IEnumerable<Guid> disclosureIds,
        IEnumerable<Guid> addOnIds)
    {
        var disclosureReferenceIdsTask = _disclosureReferenceIdService.GetReferenceIdsAsync(disclosureIds);
        var addOnReferenceIdsTask = _addOnReferenceIdService.GetReferenceIdsAsync(addOnIds);
        await Task.WhenAll(disclosureReferenceIdsTask, addOnReferenceIdsTask);

        IEnumerable<Guid> disclosuresResult = await disclosureReferenceIdsTask;
        IEnumerable<Guid> addOnsResult = await addOnReferenceIdsTask;

        await _eventPublisher.PublishAsync(new ProductPublished(productId, productReferenceId,
            disclosuresResult.ToList(), addOnsResult.ToList()));
    }

And here is my hacky solution which appears to work:

    private static void ValueChangedHandler(AsyncLocalValueChangedArgs<IRequestContext> asyncLocalValueChangedArgs)
    {
        var previousValue = asyncLocalValueChangedArgs.PreviousValue;
        var currentValue = asyncLocalValueChangedArgs.CurrentValue;
        var contextChanged = asyncLocalValueChangedArgs.ThreadContextChanged;
        if (contextChanged && currentValue == null && previousValue != null)
        {
            _requestContext.Value = previousValue;
        }
    }

FYI, this is a 4.6 runtime project running under DNX RC1 as a console app.


回答1:


What you need to do is: ExecutionContext.SuppressFlow();
That will stop raising event valueChangedHandler when your thread context is lost, as result you will not get NULL values, also it will stop raising event when new ThreadContext is created and data is copied to this.

private static void ValueChanged(AsyncLocalValueChangedArgs<string> obj)
{
    Console.WriteLine(obj.CurrentValue);
}

public static void Main(string[] args)
{
    ExecutionContext.SuppressFlow();
    AsyncLocalContext.Value = "Main";
    Task.Run(() =>
        {
            AsyncLocalContext.Value = "Test1";
        }).Wait();

    Console.WriteLine("Main: " + AsyncLocalContext.Value);
}

Output is:

Main
Test1
Main: Main

If we comment ExecutionContext.SuppressFlow(); then will get this:

Main
Main        -- copied data to Task
Test1
            -- here is NULL when context has lost
Main: Main


来源:https://stackoverflow.com/questions/35802014/asynclocal-value-updated-to-null-on-threadcontextchanged

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