在ThreadContextChanged上,AsyncLocal值更新为null

时间:2016-03-04 17:12:08

标签: c# async-await dnx

我试图了解AsyncLocal应该如何在.Net 4.6中运行。我将一些数据放入AsyncLocal ......但是当ThreadContext更改时,它被设置为null。我使用AsyncLocal的全部原因是在等待异步操作时尝试跨线程保留/缓存此值。知道为什么会特别调用它并在上下文发生变化时设置为null吗?关于AsyncLocal的文档非常稀疏......也许我已经把它弄错了。

In [8]: res
Out[8]: 
array([Decimal('1.336968892230304730842136747E-1929'),
       Decimal('5.449748658873386365158990318E+1043'),
       Decimal('2.310919470411918843300593208E+1276'),
       Decimal('3.935786060399813919328612177E+1163'),
       Decimal('5.459985199929802328950162777E+1655'),
       Decimal('6.284598705467681430195924361E-2217'),
       Decimal('1.144104288612524663561821431E-1280'),
       Decimal('7.165867721516773886182595222E+1136'),
       Decimal('1.844064027516274886503591410E+530'),
       Decimal('1.596549217532394091033873077E-530')], dtype=object)

以下是如何调用它的示例。这是使用EventStore连接(GetEventStore.com)调用的异步事件订阅者。如果这里等待的两个任务没有做任何事情(如果没有要查找的ID)我没有问题,因为可能是任务同步运行。但如果我确实要完成这些任务的工作,我就失去了我的背景。

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

}

这是我的hacky解决方案似乎有效:

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

仅供参考,这是一个在DNX RC1下作为控制台应用程序运行的4.6运行时项目。

1 个答案:

答案 0 :(得分:0)

您需要做的是:ExecutionContext.SuppressFlow();
当您的线程上下文丢失时,这将停止引发事件valueChangedHandler,因此您将不会获得NULL值,也会在创建新的ThreadContext并将数据复制到此时停止引发事件。

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

输出是:

Main
Test1
Main: Main

如果我们发表评论ExecutionContext.SuppressFlow();,那么就会得到:

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