jueves, 25 de septiembre de 2014

Receta Multithreading en C# No. 2-2: Bloqueo de una Aplicación a partir de un Deadlock

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

En esta receta multithreading en C# aprenderemos más acerca del bloqueo mutuo (o deadlock, en inglés). Debemos considerar tener conocimiento dentro de nuestra caja de herramientas para evitar o prevenir este problema tan común en recursos compartidos accedidos por múltiples threads. Veremos un ejemplo de uso en donde quedará resaltado la prevención de un bloqueo mutuo, y además, demostraremos la situación en donde se produce este tipo de bloqueo. En este último caso será evidente que la aplicación se quedará bloqueada y la única forma de finalizar su ejecución es través del cierre abrupto de la misma desde la línea de comandos.

1. Problema

Hacer una demostración de un bloqueo mutuo (o deadlock, en inglés) en el intento de acceso concurrente por parte de múltiples threads a una región crítica.

2. Solución

Para realizar la demostración de bloqueo mutuo utilizaremos dos objetos que intentarán acceder a una región crítica. Para esto tendremos que usar la palabra clave lock sobre dos objetos de bloqueo. (En la sección práctica quedará mejor ilustrada esta situación.)

3. Discusión de la Solución

Para definir un bloqueo mutuo parafrasearé la definición de este concepto encontrado en [2]:

Un bloqueo mutuo o deadlock ocurre, por ejemplo, cuando un proceso PA intenta acceder a un recurso ocupado por sí mismo o por otro proceso -PB- que también está intentando acceder al recurso ocupado por el proceso PA. Esta interpretación la podemos apreciar (seguramente mejor) en la Figura 1:
Esquema gráfico de un bloqueo mutuo
Observemos en la Figura 1 como se crea un dialelo cuando el proceso Proceso A se pone en espera del recurso Recurso 2 ocupado por el proceso Proceso B. De forma análoga, el Proceso B espera a que se libera el recurso Recurso 1 ocupado por el proceso Proceso A. Todo este dialelo hace que se produzca lo que hemos definido como bloqueo mutuo.

También es apropiado agregar que un bloqueo mutuo se puede producir inclusive cuando un proceso que se haya en espera de un recurso ocupado por otro proceso, y que este último se haya en la misma situación, es decir, esperando a que se libere un recurso que está siendo utilizado por cualquier otro proceso en el sistema.

Para no extendernos más en esta sección teórica, sugiero la lectura del artículo en Wikipedia Dealock [2] para obtener más detalles acerca de las condiciones en que puede generar un bloqueo mutuo, su detección, su prevención, entre otros tópicos relevantes.

4. Práctica: Código C#

Ahora sí creemos un ejemplo de uso en el que podemos demostrar el bloqueo mutuo generado por el intento de acceso a una región crítica por múltiples (2) threads.

En el método Bloquear (líneas 62-70) realizamos un bloqueo sobre la instancia object bloqueo1 para obtener el control sobre la región crítica de las líneas 64-69. Dentro esta misma región crítica, esperamos durante un segundo (línea 66), y luego bloqueamos la región crítica con la instancia de object bloqueo2. Este es el funcionamiento de este método.


Continuando, dentro del método Main (líneas 8-60) ocurren las siguientes acciones:
  • Líneas 14-15: Creación de dos instancias de object.
  • Línea 19: Creación de una instancia de Thread anónima que invoca de forma indirecta el método Bloquear en el thread recién creado. Seguidamente se invoca el método Start para iniciar la ejecución del método adyacente. Cuando haya ocurrido esta llamada al método en cuestión no se generará ningún bloqueo hasta el momento.
  • Línea 23: Obtenemos el control de la región crítica usando la instancia de object bloqueo2. La región crítica comprende las líneas 23-40.
  • Línea 33: Con la invocación del método TryEnter de la clase Monitor damos un tiempo de espera (5 segundos, exactamente) mientras que se intenta obtener el control con el objeto object bloqueo1. Veremos que una vez ejecutemos esta aplicación de demostración, el tiempo de espera se supera. Sin embargo, podemos notar que el uso de TryEnter evita un bloqueo permanente de la aplicación debido a que bloqueo1 se encuentra en uso por la invocación de Bloquear hecha por el thread anónimo creado en la línea 19.
Avanzando en la documentación del código fuente, la segunda parte de demostración consiste en:
  • Línea 45: Creación de un nuevo thread anónimo e invocación del método adyacente Bloquear.
  • Línea 49: Nuevo Intento de obtención del control de la región crítica con bloqueo2.
  • Línea 55: Aquí es dónde ocurre el bloqueo mutuo debido a que se está intentando acceder a bloqueo1 que aún está intentando acceder a la región crítica en la ejecución del método Bloquear.
Cuando compilemos y ejecutemos esta aplicación, se hará evidente el bloqueo. Hagámoslo:

Compilación:

  1. csc /target:exe BloqueoMutuo.cs

Ejecución assembly:

  1. .\BloqueoMutuo.exe


> Prueba de ejecución (local):
Ejecución assembly BloqueoMutuo.exe
Figura 2. Ejecución assembly BloqueoMutuo.exe.

4. Conclusiones

Ya hemos visto cómo puede generarse un bloqueo mutuo (o deadlock) con un programa tan sencillo como el mostrado en la sección 4. Esto podemos tomarlo como una señal ante problemas más complejos que requieren no sólo decenas sino cientos de procesos trabajando en paralelo y que la generación de bloqueos mutuos no es algo que debamos depreciar, todo lo contrario, nuestros diseños deben contar con una arquitectura multithreading que conciba todas (en la mayor medida) las posibles fuentes de esta situación que conlleva a problemas fatales y críticos en el desempeño y funcionamiento de toda la aplicación. En la próxima receta multithreading aprenderemos manejar las excepciones generadas por threads.

5. Glosario

  • Aplicación
  • Bloqueo mutuo
  • Deadlock
  • Liberación
  • Multithreading
  • Región crítica
  • Thread

6. Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: Deadlock - Wikipedia, the free encyclopedia - https://en.wikipedia.org/wiki/Deadlock


S

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.