Task.Run中的ASP.NET HttpContext.Current

Ale*_* Dn 14 asp.net asp.net-mvc asynchronous httpcontext threadpool

我有一个在ASP.NET MVC应用程序中使用的以下代码示例.此代码的目的是为排队一些长时间运行的操作创建"即发即弃"请求.

public JsonResult SomeAction() {
   HttpContext ctx = HttpContext.Current;            

   Task.Run(() => {
       HttpContext.Current = ctx;
       //Other long running code here.
   });

   return Json("{ 'status': 'Work Queued' }");
}
Run Code Online (Sandbox Code Playgroud)

我知道这不是在异步代码中处理HttpContext.Current的好方法,但是目前我们的实现不允许我们做其他事情.我想了解这段代码有多危险......

问题:理论上可以在Task.Run中设置HttpContext,将上下文设置为另一个请求吗?

我想是的,但我不确定.我是如何理解的:Request1是从线程池中的Thread1处理的,然后当Thread1绝对处理另一个请求(Request2)时,Task.Run中的代码将设置从Request1到Request2的上下文.

也许我错了,但我对ASP.NET内部的了解不允许我正确地理解它.

谢谢!

Ond*_*dar 10

让我在你身上碰到一点内幕:

public static HttpContext Current
{
    get { return ContextBase.Current as HttpContext; }
    set { ContextBase.Current = value; }
}

internal class ContextBase
{
    internal static object Current
    {
        get { return CallContext.HostContext; }
        set { CallContext.HostContext = value; }
    }
}

public static object HostContext
{
    get 
    {
        var executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
        object hostContext = executionContextReader.IllogicalCallContext.HostContext;
        if (hostContext == null)
        {
            hostContext = executionContextReader.LogicalCallContext.HostContext;
        }
        return hostContext;
   }
   set
   {
        var mutableExecutionContext = Thread.CurrentThread.GetMutableExecutionContext();
        if (value is ILogicalThreadAffinative)
        {
            mutableExecutionContext.IllogicalCallContext.HostContext = null;
            mutableExecutionContext.LogicalCallContext.HostContext = value;
            return;
        }
        mutableExecutionContext.IllogicalCallContext.HostContext = value;
        mutableExecutionContext.LogicalCallContext.HostContext = null;
   }
}
Run Code Online (Sandbox Code Playgroud)

所以

var context = HttpContext.Current;
Run Code Online (Sandbox Code Playgroud)

等于(伪代码)

var context = CurrentThread.HttpContext;
Run Code Online (Sandbox Code Playgroud)

在你的Task.Run内心发生这样的事情

CurrentThread.HttpContext= context;
Run Code Online (Sandbox Code Playgroud)

Task.Run将使用线程池中的线程启动新任务.所以你要告诉你的新线程"HttpContext属性"引用了起始线程"HttpContext属性" - 到目前为止一切都很好(以及你的起始线程完成后你将要面对的所有NullReference/Dispose异常).问题是如果你的内心

//Other long running code here.
Run Code Online (Sandbox Code Playgroud)

你有这样的陈述

var foo = await Bar();
Run Code Online (Sandbox Code Playgroud)

一旦你等待,你的当前线程将返回到线程池,并在IO完成后从线程池中获取新线程 - 想知道它的"HttpContext属性"是什么,对吧?我不知道:)很可能你会以NullReferenceException结束.


sco*_*732 5

您将在这里遇到的问题是,当请求完成时,HttpContext将被丢弃。由于您不等待Task.Run的结果,因此实际上是在HttpContext的处置与它在任务中的使用之间创建竞争条件。

我很确定您的任务将遇到的唯一问题是NullReferenceException或ObjectDisposedException。我看不出有任何方式可以意外窃取另一个请求的上下文。

另外,除非您要在任务中处理和记录异常,否则您的遗忘和遗忘将会抛出,并且您永远不会知道。

签出HangFire或考虑使用消息队列来处理来自单独进程的后端作业。