我有一个在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' }");
}
我知道这不是在异步代码中处理HttpContext.Current的好方法,但是目前我们的实现不允许我们做其他事情。 我想了解这段代码有多危险......
问题:理论上可以在Task.Run中设置HttpContext,将上下文设置为另一个请求吗?
我想是的,但我不确定。我怎么理解: Request1由线程池中的Thread1处理,然后当Thread1绝对处理另一个请求(Request2)时,Task.Run中的代码将设置从Request1到Request2的上下文。
也许我错了,但我对ASP.NET内部的了解不允许我正确理解它。
谢谢!
答案 0 :(得分:9)
让我对你说一点内幕:
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;
}
}
所以
var context = HttpContext.Current;
等于(伪代码)
var context = CurrentThread.HttpContext;
并在Task.Run
内部发生这样的事情
CurrentThread.HttpContext= context;
Task.Run
将使用线程池中的线程启动新任务。所以你要告诉你的新线程" HttpContext属性"引用起始线程" HttpContext属性" - 到目前为止一切顺利(以及你的起始线程完成后你所面临的所有NullReference / Dispose异常)。问题在于你的
//Other long running code here.
您有
之类的陈述var foo = await Bar();
一旦你等待,你的当前线程将返回到线程池,并在IO完成后从线程池中获取新线程 - 想知道它的" HttpContext属性"是正确的 ?我不知道:)很可能你会以NullReferenceException结束。
答案 1 :(得分:4)
您将遇到的问题是HttpContext将在请求完成时进行处理。由于您没有等待Task.Run的结果,因此您实际上是在处理HttpContext和它在任务中的使用之间创建竞争条件。
我很确定您的任务遇到的唯一问题是NullReferenceException或ObjectDisposedException。我没有看到任何可能意外窃取另一个请求的上下文的方法。
另外,除非你正在处理&在您的任务中记录异常,您的火灾和遗忘将会抛出,您永远不会知道它。
查看HangFire或考虑使用消息队列来处理来自单独进程的后端作业。