Cambiar el tamaño de la pila en .NET


Si bien lo mejor es no modificar el tamaño de la pila (stack) para tener una ejecución confiable, hay algunos casos en los que puede resultar conveniente que se cambie el tamaño de la pila, ya que si se intenta utilizar más memoria de la que está disponible en la pila se lanzaría una StackOverflowException que no se puede manejar. Tales casos incluyen, aunque no son lo únicos: la implementación de algún algoritmo recursivo de gran profundidad, una mejor utilización de la asignación de pila (stackalloc), así como la optimización en el uso de hilos múltiples.

Pero tenga presente que modificar el tamaño de a pila es algo delicado, y siempre se debe cuidar el especificar un tamaño tan pequeño como sea posible.

EDITBIN

Para modificar el tamaño de la pila en C++ se utiliza la opción “/stack” del vinculador (linker). Pero para los binarios creados con compiladores de lenguajes .NET, como C#, se tiene que recurrir a la herramienta EDITBIN.EXE.

EDITBIN es una herramienta de línea de comandos que viene con Visual Studio, la cual se utiliza para modificar binarios en formato COFF (Common Object File Format). Para el caso que nos interesa utilizaremos la opción /STACK, de forma similar al vinculador de C++:

EDITBIN.EXE /STACK:reserve[,commit] <archivo>

La opción /STACK sólo se aplica a un archivo ejecutable (EXE). El tamaño de la pila se establece en bytes.

El argumento reserve especifica la asignación total de memoria virtual de la pila. El valor especificado se redondeará a los 4 bytes más próximos.

El argumento opcional commit está sujeto a interpretación por el sistema operativo. En Windows NT, Windows 95 y Windows 98, commit especifica la cantidad de memoria física que se debe asignar de una sola vez. La memoria virtual comprometida hace que se reserve espacio en el archivo de paginación. Si se asigna un valor más alto a commit, se ahorrará tiempo cuando la aplicación necesite más espacio de la pila, pero aumentarán los requisitos de memoria y, posiblemente, el tiempo de inicio.

Así por ejemplo, para reservar un tamaño máximo de pila de 2MB ejecutaríamos:

EDITBIN.EXE /STACK:2097152 ejecutable.exe

Este método nos obliga a alterar el ejecutable después de cada compilación, cosa que se pude automatizar utilizando el evento post-build.

Tome en cuenta que se trata de una asignación estática que afecta al ejecutable en sí, cuando talvez sería más conveniente una solución dinámica, en tiempo de ejecución y que afecte a un solo hilo a la vez.

Constructor Thread

Se puede especificar programáticamente el tamaño máximo de pila para un hilo utilizando las sobrecargas del constructor de la clase Thread, disponibles a partir de la versión 2.0 del .NET Framework.

Thread myThread = new Thread(myThreadDelegate, maxStackSize);

Donde:

  • MyThreadDelegate: es un delegado ThreadStart o ParameterizedThreadStart que representa los métodos a ser invocados cuando el hilo empieza a ejecutarse.
  • maxStackSize: es el tamaño de pila máximo a ser utilizado por el hilo, si es 0 utiliza el tamaño predeterminado especificado en el encabezado del ejecutable. Si no es ningún múltiplo del tamaño de página establecido por el sistema operativo, se redondea al múltiplo mayor siguiente del tamaño de página.

Este método sólo funciona con sistemas operativos Windows XP, Windows 2003 y posteriores, en sistemas anteriores no tendrá ningún efecto por lo que se utiliza el tamaño de la pila especificado en la cabecera del ejecutable.

Nota: Si maxStackSize es menor que el tamaño de pila mínimo, se utiliza el tamaño de pila mínimo. Si especifica un tamaño de pila muy pequeño y la pila está muy limitada, podría necesitar deshabilitar el sondeo de desbordamiento de pila, ya que el propio sondeo puede provocar un desbordamiento de pila. Para deshabilitar el sondeo del desbordamiento de pila, agregue lo siguiente al archivo de configuración de la aplicación.

<configuration>
  <runtime>
    <disableStackOverflowProbing enabled="true"/>
  </runtime>
</configuration>

 

Kernel32.dll

También se puede especificar el tamaño de un hilo utilizando la API de Windows. Para ello empleamos PInvoke con kernel32.dll y ejecutando su método CreateThread para crear dicho hilo con el tamaño especificado en su segundo parámetro.

myThread = CreateThread(
    IntPtr.Zero, // atributo de seguridad
    maxStackSize, // tamaño máximo de pila
    myThreadDelegate, // delegado del hilo
    pDataArg, // argumento para el delegado
    0, // bandera predeterminada
    out dwThreadId); // identificador del hilo

Dado que se trata del uso del API Win32, este método puede utilizarse con cualquier versión del .NET Framework y funciona con cualquier sistema Windows compatible. Por otro lado, tenga en cuenta que se trata de “código inseguro” (unsafe-code).

Más información sobre este último método en Creating Threads.

Acerca de Willy Mejia

Developer, Techie, Human... http://about.me/willyxoft
Esta entrada fue publicada en .NET. Guarda el enlace permanente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s