我被玩弄的.NET异步功能一点点,并与我真的无法解释的情况出现了。 当执行同步ASP.NET MVC控制器内部下面的代码
var t = Task.Factory.StartNew(()=>{
var ctx = System.Web.HttpContext.Current;
//ctx == null here
},
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()
);
t.Wait();
ctx
是null
的委托范围内。 现在,我的理解,上下文应该当您使用恢复TaskScheduler.FromCurrentSynchronizationContext()
任务调度。 那么,为什么不在这里? (我可以,顺便说一句,看到代表得到了相同的线程同步执行)。
此外,从MSDN ,一个TaskScheduler.FromCurrentSynchronizationContext()
应该表现如下:
排队到返回调度所有任务实例将通过对这种情况下的Post方法调用执行。
然而,当我使用此代码:
var wh = new AutoResetEvent(false);
SynchronizationContext.Current.Post(s=> {
var ctx = System.Web.HttpContext.Current;
//ctx is set here
wh.Set();
return;
},null);
wh.WaitOne();
上下文实际上是设置。
我知道,这个例子是有点做作,但我真的很想知道发生了什么,以增加我对.NET异步编程的理解。
您的意见似乎是正确的,这是一个有点令人费解。 您可以指定调度程序“TaskScheduler.FromCurrentSynchronizationContext()”。 此相关联的新的“SynchronizationContextTaskScheduler”。 现在,如果你看看这个类,它使用:
因此,如果任务调度器可以访问相同的“同步环境”,而应参考“LegacyAspNetSychronizationContext”。 所以肯定似乎HttpContext.current不能为空。
在第二种情况下,当你使用一个SychronizationContext(参考: MSDN文章 )的线程的上下文与任务共享:
“的SynchronizationContext的另一个方面是,每个线程有一个‘当前的’上下文中的线程的上下文不一定是唯一的;其上下文实例可以与其他线程共享”
SynchronizationContext.Current通过LegacyAspNetSychronizationContext在这种情况下提供,并且在内部具有向的HttpApplication的参考。
当早报方法调用注册的回调,它调用HttpApplication.OnThreadEnter,最终导致当前线程的上下文设置为HttpCurrent.Context:
这里引用的所有类被定义为在框架内,并使其成为一个有点难以进一步调查。
PS:举例说明,其实点“LegacyAspNetSynchronizationContext”既SynchornizationContext对象:
你的结果是奇怪 - 你肯定没有什么别的事情?
你的第一个例子(带Task
),只能是因为Task.Wait()
可以运行任务体“内联”。
如果你把一个断点在任务拉姆达,并期待在调用堆栈,你会看到拉姆达正在从内部调用Task.Wait()
方法-没有并发问题。 由于任务正在用平常心同步方法调用执行, HttpContext.Current
必须返回相同的值,因为它会从其他地方在您的控制器方法。
你的第二个例子(带SynchronizationContext.Post
)将死锁和你的拉姆达将永远不会运行。
这是因为您正在使用AutoResetEvent
,不“知道”你的任何Post
。 要调用WaitOne()
将阻止线程,直到AutoResetEvent
被Set
。 与此同时,该SynchronizationContext
正在等待的线程是自由的,以便运行拉姆达。
由于线程阻塞WaitOne
,张贴拉姆达永远不会执行,这意味着AutoResetEvent
将永远不会被设置,这意味着WaitOne
将永远无法满足。 这是一个僵局。
我在google搜索的HttpContext信息前一段时间。 我发现这一点:
http://odetocode.com/articles/112.aspx
这是关于线程和HttpContext的。 有很好的解释:
该CallContext中提供极为相似服务线程本地存储(除CallContext中可以远程调用过程中执行一些额外的魔法)。 线程本地存储是一个概念,其中一个应用程序域中的每个逻辑线程都有一个唯一的数据槽,以保持数据的特定本身。 线程不共享的数据,一个线程不能修改本地到不同的线程中的数据。 ASP.NET中,选择一个线程来执行传入的请求后,存储在线程的本地存储的当前请求上下文的引用。 现在,无论在哪里,同时执行(业务对象,数据访问对象)线程去,上下文就在附近,很容易地检索。
知道上面我们可以声明如下:如果,在处理请求,执行移动到一个不同的线程(通过QueueUserWorkItem,或异步委托,作为两个实例),HttpContext.Current将不知道如何以检索当前上下文,并且将返回null。 你可能会认为解决该问题的一种方式是传递给工作线程的引用
所以,你必须通过创建一些变量引用您的HTTPContext.Current和这个变量将你会在你的代码中创建其他线程不客气。
文章来源: Why is ASP.NET HttpContext.Current not set when starting a task with current synchronization context