问题描述
我试图了解AsyncLocal应该如何在.Net 4.6中工作.我正在将一些数据放入AsyncLocal ...中,但是当ThreadContext更改时,它将设置为null.我使用AsyncLocal的全部原因是试图在等待异步操作时跨线程保留/缓存此值.知道为什么会随着上下文的变化专门调用它并将其设置为null吗?关于AsyncLocal的文档非常稀疏...也许我把它弄错了.
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;
}
}
这里是如何调用它的示例.这是一个异步事件订阅服务器,正在使用EventStore连接(GetEventStore.com)调用它.如果这里等待的两个任务什么都不做(如果没有要查找的ID),那么我就没有问题,因为该任务可能是同步运行的.但是,如果我确实有完成这些任务的工作,那么我会失去上下文.
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;
}
}
仅供参考,这是一个在DNX RC1下作为控制台应用程序运行的4.6运行时项目.
FYI, this is a 4.6 runtime project running under DNX RC1 as a console app.
推荐答案
您需要做的是:ExecutionContext.SuppressFlow();
当您的线程上下文丢失时,它将停止引发事件valueChangedHandler
,结果您将不会获得NULL
值,并且在创建新的ThreadContext并将数据复制到该线程时,它将停止引发事件.
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);
}
输出为:
Main
Test1
Main: Main
如果我们评论ExecutionContext.SuppressFlow();
,则会得到以下提示:
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
这篇关于AsyncLocal值在ThreadContextChanged上更新为null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!