Controller.HttpContext (HttpContextBase) 线程安全吗?



如果您在(非核心(ASP.NET MVC 5 项目中具有以下代码:

using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Mvc;
namespace ASPApp.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public Task<ActionResult> UnSafeThreadAccess()
{
PrintThreadId("UnSafeThreadAccess entry");
var synchronisationContext = SynchronizationContext.Current;
return Task.Delay(2).ContinueWith(_ =>
{
PrintThreadId("UnSafeThreadAccess continueWith");
var controllerContext = HttpContext.Request.QueryString["q"];
var content = $"Responding to {System.Web.HttpContext.Current.Request.QueryString["q"]}";
return Content(content) as ActionResult;
});
} 
private static void PrintThreadId(string threadName)
{
Trace.WriteLine($"{threadName}: {Thread.CurrentThread.ManagedThreadId}");
}
}
}

我知道访问ContinueWith内部的System.Web.HttpContext.Current是不好的,因为它绑定到控制器的入口线程。此代码的要点是说明为什么在此示例中需要等待Task.Delay

但是,我有点惊讶控制器的HttpContext属性可以在ContinueWith内部访问。这怎么可能?我知道属性的类型不是HttpContext而是HttpContextBase,但是我期望这只会调用静态 HttpContext 实例。

但是,我有点惊讶控制器的HttpContext属性可以在ContinueWith内部访问。这怎么可能?[...],但是我期望这只会调用静态HttpContext实例。

挖掘源代码会发现Controller.HttpContext不是对HttpContext.Current的简单调用,而是从请求上下文(System.Web.Routing.RequestContext(中检索HTTP上下文,该上下文实际上存储了对当前HttpContextBase实例的引用:

https://github.com/aspnet/AspNetWebStack/blob/v3.2.6/src/System.Web.Mvc/Controller.cs#L87
https://github.com/aspnet/AspNetWebStack/blob/v3.2.6/src/System.Web.Mvc/ControllerContext.cs#L71
https://github.com/Microsoft/referencesource/blob/4.6.2/System.Web/Routing/RequestContext.cs#L23

从这个意义上说,从请求的入口线程以外的另一个线程访问Controller.HttpContext是安全的。

但是,MSDN 对HttpContext说了以下内容:

当 HttpRequest 完成时,此对象已准备好进行垃圾回收。它在请求完成后的使用可能会导致未定义的行为,例如 NullReferenceException。

此对象仅在 ASP.NET 控制的线程中可用。在后台线程中使用可能会导致未定义的行为。

这不是最精确的措辞,但我假设至少不会改变对象状态的操作(如读取请求查询字符串(在后台线程中必须是安全的。(当然,在请求完成之前。

最新更新