赞助商
由于项目需要,刚才打算为ASP.NET MVC应用程序增强ControllerFactory的功能,因此翻出了ASP.NET MVC的源代码开始阅读其DefaultControllerFactory。代码不多,很容易理解,不过读着读着便发现了问题,因为我发现DefaultControllerFactory不是线程安全的。

  线程安全,故名思义便是在多线程的环境下,是否可以正常工作的意思。以前也看过ASP.NET MVC的源代码,印象中在默认情况下,整个应用程序会共享同一个DefaultControllerFactory实例(刚才又确认了一下的确是这样的),这便要求这个类的所有操作——至少是对外公开的接口都需要是线程安全的。

  线程安全与否有时候并不容易发现,但是某些状况下我们还是可以总结出一些“实现模式”,遵循或违反这些模式,则这个组件很可能不是线程安全的。例如,如果满足了Share Nothing,至少是No Shared Writing,这个组件很可能就是线程安全的。不过DefaultControllerFactory可不是这样,例如它的CreateController方法:
  1. public virtual IController CreateController(RequestContext requestContext, string controllerName) {
  2.     if (requestContext == null) {
  3.         throw new ArgumentNullException("requestContext");
  4.     }
  5.     if (String.IsNullOrEmpty(controllerName)) {
  6.         throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
  7.     }
  8.     RequestContext = requestContext;
  9.     Type controllerType = GetControllerType(controllerName);
  10.     IController controller = GetControllerInstance(controllerType);
  11.     return controller;
  12. }
复制代码
请看这行代码RequestContext = requestContext;这是一句为自身实例属性RequestContent赋值的语句,它自然是要被接下来的GetControllerType和GetControllerInstance方法所访问。当我们发现了类似的“写属性”的语句应该有所警觉,因为它往往意味着这个组件不是线程安全的。如果是线程安全的代码,则往往需要将数据统统作为方法的参数进行传递。

  那么,既然DefaultControllerFactory不是线程安全的,那么为什么在使用过程中却没有发生任何问题呢?幸运的是,在普通使用过程中,我们几乎无法遇到这样的逻辑。例如在GetControllerType中,对RequestContext属性读取的逻辑是这样的:
  1. protected internal virtual Type GetControllerType(string controllerName) {
  2.     ...

  3.     // first search in the current route's namespace collection
  4.     object routeNamespacesObj;
  5.     Type match;
  6.     if (RequestContext != null &&
  7.         RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out routeNamespacesObj)) {
  8.         ...
  9.     }

  10.     ...
  11. }
复制代码
RequestContext永远不可能为null,而且我也从来没有见过谁向RouteData中的DataTokens集合中填充过数据(这需要在应用程序一开始进行Route Mapping时指定),因此这段逻辑“永远”是略过的。GetControllerInstance方法情况略有不同:
  1. protected internal virtual IController GetControllerInstance(Type controllerType) {
  2.     if (controllerType == null) {
  3.         throw new HttpException(404,
  4.             String.Format(
  5.                 CultureInfo.CurrentUICulture,
  6.                 MvcResources.DefaultControllerFactory_NoControllerFound,
  7.                 RequestContext.HttpContext.Request.Path));
  8.     }

  9.     ...
  10. }
复制代码
只有在ControllerType为null的时候,为了抛出一个404异常才会涉及到ReqeustContext属性——只是,这时候又有谁去注意这个呢?
赞助商
赞助商
TOP