如果(非核心)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}");
}
}
}
我知道在System.Web.HttpContext.Current
内部访问ContinueWith
是不好的,因为它绑定到控制器的入口线程。这段代码的重点是说明为什么在此示例中必须等待Task.Delay
。
但是我很惊讶可以在HttpContext
内部访问控制器的ContinueWith
属性。这怎么可能?我知道属性的类型不是HttpContext
,而是HttpContextBase
,但是我期望这只会调用静态HttpContext实例。
答案 0 :(得分:1)
但是我很惊讶可以在
HttpContext
内部访问控制器的ContinueWith
属性。这怎么可能? [...],但是我期望这只会调用静态HttpContext
实例。
在源中进行的挖掘表明,Controller.HttpContext
不是对HttpContext.Current
的简单调用,但是HTTP上下文是从请求上下文(System.Web.Routing.RequestContext
)中检索的,该请求上下文实际上存储了对当前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 says关于HttpContext
的以下内容:
当HttpRequest完成时,此对象已准备好进行垃圾回收。请求完成后使用它可能会导致不确定的行为,例如NullReferenceException。
此对象仅在ASP.NET控制的线程中可用。在后台线程中使用可能导致未定义的行为。
这不是最精确的措词,但我认为至少不改变对象状态的操作(例如读取请求查询字符串)在后台线程中必须是安全的。 (当然,在请求完成之前。)