2012-08-02 19 views
5

Me encontré con una situación en la que, de acuerdo con un minivolcado, ciertos archivos están causando un desbordamiento de pila en un analizador sintáctico de descenso recursivo. Lamentablemente, no puedo tener en mis manos un ejemplo de un archivo que hace esto para reproducir el problema (el cliente tiene problemas de confidencialidad), lo que me deja un poco desanimado al diagnosticar el problema real por el momento.¿Cómo puedo prevenir o recuperar un desbordamiento de pila en un subproceso de trabajador?

Claramente, el analizador necesita algo de atención, pero ahora mi principal prioridad es simplemente mantener el programa en ejecución. Como medida provisional, ¿qué puedo hacer para evitar que esto reduzca todo el programa?

Mi primera opción sería encontrar la manera de anticipar que me estoy quedando sin espacio en la pila para poder abortar con gracia el analizador antes de que ocurra el desbordamiento. No analizar el archivo es una opción aceptable. La segunda opción sería dejar que suceda, detectar el error y registrarlo, luego continuar con el resto de los datos.

El análisis está sucediendo en un bucle Parallel.ForEach(). Estoy dispuesto a cambiar eso por otro enfoque si eso ayuda.

EDIT: Lo que sería realmente asesino es si tan sólo pudiera obtener el tamaño de la pila del subproceso actual y la posición del puntero de pila. es posible?

EDIT 2: Finalmente logré escurrir un archivo de muestra de alguien y atrapar el error en un depurador. Resulta que no es código que nos pertenece en absoluto, la excepción está en algún lugar en HtmlAgilityPack. Entonces parece que tendré que intentar encontrar una táctica completamente diferente.

+0

No estoy seguro si esto ayudará ya que lo que causa el desbordamiento de la pila no está claro (el paralelismo no debería causar esto: la recursividad podría), pero ¿ha intentado usar 'ParallelOptions.MaxDegreeOfParallelism' para limitar la cantidad de llamadas simultáneas? – Jcl

+0

Una opción es simplemente rastrear la "profundidad" actual del análisis sintáctico y fianza si es demasiado alta. – dlev

+0

@dlev Me gustaría tener más detalles, sin embargo. La documentación de .NET sugiere eso, pero ¿cómo elijo una profundidad máxima apropiada, dado que tanto los marcos de pila como la pila de llamadas en su conjunto pueden tener diferentes tamaños? –

Respuesta

3

La pila tiene un límite de 1 MB por defecto en CLR de escritorio, pero usted can increase it.

Puede usar un continuation passing style para usar el montón en lugar de la pila.

En C# 5.0, hay un mecanismo asincrónico proporcionado por un compilador que automatiza este proceso. No he intentado esto con la última versión. Como lo menciona Alex, no hay soporte para la optimización de la cola de llamada en C#, y esta podría ser una razón lo suficientemente grande para adoptar F # para analizar problemas. Aquí es some material on lexing and parsing with F#. tu caso es distinto, como se demuestra en this article.

También había necesidad de detección de ciclo de gráfico para hacer su programa de sólidos en el presence of bad inputs.

Como una forma de recopilar más información, puede navegar a través de un entero acumulador que rastrea qué tan profundo es su pila de llamadas. Esto no se traducirá directamente en la memoria consumida por dicha pila de llamadas, pero le da una idea general. Por ejemplo, puede lanzar y atrapar su propia excepción cuando ese número es mayor que algún umbral predefinido o configurable por el usuario.

public void Recursive(int acc) 
{ 
    if (acc > myLimit) 
     throw new MyOverflowException(acc); 

    Recursive(acc+1); 
} 

y luego a la llamada in situ:

try { Recursive(0); } catch (MyOverflowException) { /* handle it*/ } 

a lo solicitado, voy a enlazar hasta que el blog fabuloso por Eric Lippert en this very topic.

+1

Un pequeño detalle sería bueno. –

+0

@GregC Esto es algo que estoy considerando hacer como una solución a más largo plazo. Pero en este momento estoy buscando una solución temporal, y ese sería un refactor bastante grande. –

+1

Me refiero a dar un ejemplo de un estilo de continuación de paso, y tal vez incluso demostrar cómo usa menos stack. –

0

Un hilo estrellarse debido al SOE reducirá todo el proceso y no hay mucho que pueda hacer al respecto.

Como medida de recuperación, en su lugar podría iniciar el analizador como un proceso separado y configurar un mecanismo de IPC para comunicarse con el niño. De esta forma, el proceso del niño es libre de morir sin afectar el proceso principal.

Cuestiones relacionadas