2009-04-09 5 views
12

usuario pulsa spawn.aspx página que a su vez genera una media docena de hilos, representando las páginas utilizando todaEl uso de un HttpContext través de hilos

((System.Web.IHttpHandler)instance).ProcessRequest(reference to spawn's HTTPContext); 

No se preocupe por el hecho de que ASP.Net es aparentemente enviando al usuario 7 respuestas para 1 solicitud, esa parte se maneja y solo se envía una respuesta.

El problema es que, en un entorno de alto tráfico (nuestro entorno de producción) con muchos hilos (quad-quads) obtenemos un 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 

No podemos duplicarlo en otro lugar. Mi compañero de trabajo cree que esto se debe a que estoy reutilizando el HTTPContext original y pasándolo a los otros hilos, y que no es Thread-Safe.

Siguiendo esta lógica, he intentado hacer un nuevo HTTPContext para pasar a los hilos. Pero algunas partes aparentemente no se "combinarán". Específicamente, necesito obtener el objeto Session en el nuevo HTTPContext. Me imagino que me gustaría tener otras partes también, como Cache. Para el registro HTTPContext.Current.Session.IsSynchronized es falso.

Mis preguntas son:

  1. ¿Cree que el error es el uso de HttpContext través de hilos?
  2. ¿Cómo puedo solucionarlo?
  3. Si la solución está duplicando el HTTPContext para cada subproceso, ¿cómo puedo obtener la sesión (y el caché) en el nuevo? Solicitud y respuesta vienen en el ctor, pero la sesión no es configurable.

Edit: Más detalles

Así que volviendo a esta declaración: "No se preocupe por el hecho de que ASP.Net es aparentemente enviando al usuario 7 respuestas durante 1 solicitud, esa parte se maneja y solo se envía una respuesta ". Gran admirador de Raymond Chen, estoy de acuerdo contigo: "Ahora tienes dos problemas" es una afirmación razonable en ausencia de más información.

Lo que sucede en realidad es que estoy compilando un documento de Excel para enviar de vuelta. En la página spawn.aspx está configurando cierta información de estado, incluido el hecho de que se está renderizando para sobresalir y el objeto para hacer la representación. Cada página generada obtiene esa información, y se bloqueará hasta que sea su turno de renderizar al objeto. Si, literalmente, tiene el siguiente aspecto:

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); 
} 

Pero todo el proceso hasta ese momento - el acceso de base de datos, control de jerarquía, todo lo que se hace en paralelo. Y hay mucho de eso, lo suficiente como para dividirlo en paréntesis y, al mismo tiempo, dejar que se bloquee en Render reducirá el tiempo general en más de la mitad.

Y la mejor parte es que no hay que reescribir nada para el renderizado en Excel. Todos los controles saben cómo renderizarse para sobresalir, y puede visitar cada página generada independientemente (ese es el 'caso normal' en realidad - el informe de Excel es solo una agregación de todas las páginas generadas.)

Así que pensé El resultado final iba a ser "no se puede hacer esto, se debe replantear el enfoque", pero al menos tuve que intentarlo, porque el hecho de que todo funciona tan bien sin duplicar lógica o código ni tener que abstraer nada es tan perfecto. Y ese es el único problema del multi-threading, si renderizo las páginas en serie todo está bien, solo lento.

Respuesta

2

Sus compañeros de trabajo tienen razón, si un hilo bloquea un recurso y otro hilo intenta usarlo, ¡su grupo de hilos se dispara! No muy buen resultado. La mayoría de las personas lo resuelve creando nuevos objetos y pasándolos a hilos paramaterizados. Si necesita usar el mismo objeto, debe implementar un código que primero verifique si el recurso está siendo utilizado por otro hilo y espere un poco antes de volver a verificarlo. Un ejemplo sería crear un bool IsInUse que siempre verifique primero, luego su hilo lo establece en verdadero si está usando ese recurso, luego falso cuando está hecho, evitando que otros hilos intenten usar el recurso subyacente (su httpContext) . Espero que esto ayude.

+0

Si este bloqueo probablemente será difícil de arreglar, la excepción de subprocesamiento que recibe es de la clase Page, que está mutando el contexto http, a menos que pueda anular la acción en la página que está haciendo esto, y coloque un bloqueo luego el la solución de bloqueo no funcionará. – meandmycode

+1

Un gran comentario, estoy de acuerdo contigo. Mi preferencia sería pasar un objeto totalmente nuevo que derive parte de su información del contexto HTTP en el momento de la creación de las bandas de rodamiento. Eso sería a prueba de balas. –

4

Mientras que HttpContext está diseñado para manejar un contexto que no es específico de subprocesos (porque el contexto http puede comenzar en un subproceso y terminar en otro), no es implícitamente seguro para subprocesos.

Esencialmente el problema es que usted está haciendo algo que no está destinado, estas solicitudes serían múltiples en general y cada una tiene su propia aplicación Http asignada para cumplir con la solicitud, y cada una tiene su propio HttpContext.

Realmente trataría de dejar que la infraestructura asp.net delegue las solicitudes.

1

Dado que HttpContext es parte de la biblioteca .Net, esperaría que todas las funciones estáticas sean enhebrables, pero que los miembros no estáticos no son seguros para las subprocesidades al llamar contra la misma instancia del objeto. Así que espero que la próxima vez anticipe que compartir la instancia de HttpContext entre hilos tendría problemas.

¿Hay alguna forma de desacoplar las operaciones que necesita realizar en paralelo desde HttpContext? Si solo están cargando datos y escribiendo en formato CSV, ese código no tiene una dependencia necesaria en el control de usuario de ASP.NET o en el ciclo de vida de la página. Una vez que se elimine esa dependencia, puede implementar la página con un HttpHandler asíncrono, ejecutando las operaciones paralelas durante entre IHttpHandler.BeginProcessingRequest() y IHttpHandler.EndProcessingRequest().

1

Me aseguraré de que cada vez que acceda a la colección HttpContext.Current.Items, utilice un bloque de bloqueo de monitor (C#)/SyncLock (VB) en el objeto HttpContext.Current.Items.SyncRoot para envolver sus llamadas.