User hits page spawn.aspx which then spawns a half-dozen threads, rendering pages all using
((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn's HTTPContext);
Don't worry about the fact that ASP.Net is seemingly sending the user 7 responses for 1 request, that part is handled and only one response gets sent.
The problem is, in a high-traffic enviroment (our Production enviroment) with many threads (quad-quads) we get an error:
System.IndexOutOfRangeException
at System.collections.ArrayList.Add
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, DateTime utcDepTime)
at System.Web.ResponseDependencyList.AddDependencies(String[] items, String argname, Boolean cloneArray, String requestVritualPath)
at System.Web.UI.Page.AddWrappedFileDependencies(Object virtualFileDependencies)
at ASP.spawned_page_no_1_aspx.FrameworkInitialize()
at System.Web.UI.Page.ProcessRequest
We can't duplicate it elsewhere. My coworker believes this is because I'm reusing the original HTTPContext and passing it into the other threads, and that it's not Thread-Safe.
Following this logic, I've tried making a new HTTPContext to pass into the threads. But parts of it seemingly won't "combine". Specifically, I need to get the Session object into the new HTTPContext. I imagine I'd want to get other parts in as well, like Cache. For the record HTTPContext.Current.Session.IsSynchronized is false.
My questions are:
- Do you think the error is from using HTTPContext across threads?
- How can I fix it?
- If the fix is duplicating the HTTPContext for each thread, how can I get the Session (and Cache) into the new one? Request and Response come in the ctor, but Session is not settable.
Edit: More Details
So going back to this statement: "Don't worry about the fact that ASP.Net is seemingly sending the user 7 responses for 1 request, that part is handled and only one response gets sent." Huge fan of Raymond Chen, I agree with you: "Now you have two problems" is a reasonable statement in the absence of any more information.
What's actually happening is that I'm building an Excel Document to send back. In the spawn.aspx page it's setting up some state information, including the fact that it's rendering to excel, and the object to do the rendering to. Each spawned page gets that information, and will block until it's their turn to render to the object. If literally looks like this:
protected override void Render(System.Web.UI.HtmlTextWriter writer)
{
if (this.RenderToExcel)
{
Deadlocker.SpinUntilCurrent(DeadLockToken);
RenderReport(this, this.XLSWriter);
Deadlocker.Remove(DeadLockToken);
}
else
base.Render(writer);
}
But all the processing up to that point - database access, control heirarchy, all that's done in parallel. And there's a lot of it - enough that parrallizing it while still letting it block on Render will cut the overall time in over half.
And the best part of it is - nothing had to be rewritten for the Excel render. All the controls know how to render themselves to excel, and you can visit each spawned page independently (that's the 'normal case' actually - the excel report is just an aggregation of all the spawned pages.)
So I figured the end result was going to be "you can't do this, you need to rethink the approach" - but I had to at least try, because the fact that everything works so nicely without duplicating any logic or any code or having to abstract anything is just so perfect. And it's only multi-threading that's the problem, if I render the pages serially everything is fine, just slow.
See Question&Answers more detail:
os