并行执行多个Web页面(Razor)

时间:2012-03-16 15:51:27

标签: razor parallel-processing thread-safety

我正在加载并执行像这样的.cshtml文件

var webPage = WebPage.CreateInstanceFromVirtualPath(_relativeFilePath);

var httpContext = new HttpContextWrapper(HttpContext.Current);
var startPage = StartPage.GetStartPage(webPage, "_PageStart", new[] { "cshtml" });
var pageContext = new WebPageContext(httpContext, webPage, startPage);

var sb = new StringBuilder();
using (var writer = new StringWriter(sb))
{
   webPage.ExecutePageHierarchy(pageContext, writer);
}

string output = sb.ToString().Trim();

现在,因为(在我的世界中),每个.cshtml文件都是一个独立的单元,我可以让上面的代码并行运行N次,但在极少数情况下它会因下面的异常而爆炸。

System.InvalidOperationException: Stack empty.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Stack`1.Pop()
   at System.Web.WebPages.TemplateStack.Pop(HttpContextBase httpContext)
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy()
   at System.Web.WebPages.WebPage.ExecutePageHierarchy(IEnumerable`1 executors)
   at System.Web.WebPages.WebPage.ExecutePageHierarchy()
   at System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage)

如果我禁用并行化并逐个运行所有.cshtml文件,我就无法触发异常。这让我想知道WebPages的某些内部工作方式是否不是线程安全的,每次调用ExecutePageHierarchy都会以某种方式访问​​当前HttpContext

中的某些非唯一值

2 个答案:

答案 0 :(得分:4)

对于多个使用者而言,HttpContext和其他ASP.NET内部对象通常不是线程安全的。如果您的目标只是使用Razor模板引擎并行处理多个事物,我相信您可以这样做,因为Razor本身并没有直接依赖ASP.NET中的任何内容。但是,一旦你涉及.cshtml(System.Web.WebPages),你就会被ASP.NET的内在函数所限制。

答案 1 :(得分:1)

只为其他人看到这个帖子。 我做了抽象类HttpContextBase和HttpRequestBase的最小实现。然后,当我想渲染一个viewPage时,我会提供这些类的新实例而不是真实的实例。 (我没有使用模板框架的原因是我还没有发现一个线程安全的,提示任何人?)

 public class MockRequest : HttpRequestBase
{
    public override bool IsLocal { get { return false; } }
}

public class MockHttpContext : HttpContextBase
{
    private readonly IDictionary _items = new Hashtable();

    public override HttpRequestBase Request { get { return new MockRequest(); } }

    public override IDictionary Items { get { return _items; } }
}