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

时间:2018-08-23 19:55:30

标签: c# asp.net-mvc multithreading

如果(非核心)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实例。

1 个答案:

答案 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控制的线程中可用。在后台线程中使用可能导致未定义的行为。

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