为什么先从当前同步上下文任务时ASP.NET HttpContext.Current未设定(Why

2019-08-04 09:36发布

我被玩弄的.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();

ctxnull的委托范围内。 现在,我的理解,上下文应该当您使用恢复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异步编程的理解。

Answer 1:

您的意见似乎是正确的,这是一个有点令人费解。 您可以指定调度程序“TaskScheduler.FromCurrentSynchronizationContext()”。 此相关联的新的“SynchronizationContextTaskScheduler”。 现在,如果你看看这个类,它使用:

因此,如果任务调度器可以访问相同的“同步环境”,而应参考“LegacyAspNetSychronizationContext”。 所以肯定似乎HttpContext.current不能为空。

在第二种情况下,当你使用一个SychronizationContext(参考: MSDN文章 )的线程的上下文与任务共享:

“的SynchronizationContext的另一个方面是,每个线程有一个‘当前的’上下文中的线程的上下文不一定是唯一的;其上下文实例可以与其他线程共享”


SynchronizationContext.Current通过LegacyAspNetSychronizationContext在这种情况下提供,并且在内部具有向的HttpApplication的参考。

当早报方法调用注册的回调,它调用HttpApplication.OnThreadEnter,最终导致当前线程的上下文设置为HttpCurrent.Context:


这里引用的所有类被定义为在框架内,并使其成为一个有点难以进一步调查。

PS:举例说明,其实点“LegacyAspNetSynchronizationContext”既SynchornizationContext对象:



Answer 2:

你的结果是奇怪 - 你肯定没有什么别的事情?


你的第一个例子(带Task ),只能是因为Task.Wait()可以运行任务体“内联”。

如果你把一个断点在任务拉姆达,并期待在调用堆栈,你会看到拉姆达正在从内部调用Task.Wait()方法-没有并发问题。 由于任务正在用平常心同步方法调用执行, HttpContext.Current必须返回相同的值,因为它会从其他地方在您的控制器方法。


你的第二个例子(带SynchronizationContext.Post )将死锁和你的拉姆达将永远不会运行。

这是因为您正在使用AutoResetEvent ,不“知道”你的任何Post 。 要调用WaitOne()将阻止线程,直到AutoResetEventSet 。 与此同时,该SynchronizationContext正在等待的线程是自由的,以便运行拉姆达。

由于线程阻塞WaitOne ,张贴拉姆达永远不会执行,这意味着AutoResetEvent将永远不会被设置,这意味着WaitOne将永远无法满足。 这是一个僵局。



Answer 3:

我在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