miércoles, 22 de julio de 2015

Receta Multithreading en C# No. 3-3: Pool de Threads y su Grado de Paralelismo

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Clase CountdownEvent
3.2 Método static ThreadPool.QueueUserWorkItem
3.3 Clase Stopwatch
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Desarrollaremos esta nueva receta multithreading en C# para verificar el grado de paralelismo de un pool de threads. Para ello tendremos que hacer un versus entre el enfoque tradicional (uso de threads) y el pool de threads.

1. Problema

Deseamos validar el grado de paralelismo en la ejecución de operaciones asincrónicas de un pool de threads frente a la creación de threads.

2. Solución

La clase CountdownEvent y el método static ThreadPool.QueueUserWorkItem nos permitirán llevar a cabo el contraste de paralelismo entre los dos enfoques anteriores; además del cronómetro representado con la clase Stopwatch para efectuar la medición de ejecución de operaciones asincrónicas.

3. Discusión de la Solución

3.1 Clase CountDownEvent

Para estudiar la clase CountDownEvent recomiendo la lectura de Receta Multithreading en C# No. 2-6: Uso de la Clase CountdownEvent.

3.2 Método static ThreadPool.QueueUserWorkItem

En Receta Multithreading en C# No. 3-2: Exponer una Operación Asincrónica en un Pool de Threads podemos estudiar el uso del método ThreadPool.QueueWorkItem.

3.3 Clase Stopwatch

La clase Stopwatch [4] provee una serie de métodos y propiedades para medir el tiempo transcurrido de una operación. Podemos, inclusive, medir el tiempo transcurrido de múltiples intervalos. Para mediciones más precisas:
"If the installed hardware and operating system support a high-resolution performance counter, then the Stopwatch class uses that counter to measure elapsed time."
Ejemplo de uso:

Con 

Stopwatch sw = new Stopwatch();


(línea 15) creamos un nuevo objeto Stopwatch. Enseguida, línea 18, iniciamos la ejecución del cronómetro. Con Thread.Sleep(10000); simulamos un tiempo de ejecución de 10 segundos. Detenemos el cronómetro con sw.Stop() (línea 24).

Capturamos el tiempo medido (línea 27). Lo formateamos (línea 31). Finalmente mostramos el valor medido formateado en la salida estándar (línea 34).

Compilación: 


  1. csc /target:exe UsoStopwatch.cs

Ejecución assembly:


  1. .\UsoStopwatch.exe

> Prueba de ejecución: http://ideone.com/0dPeDg

> Prueba de ejecución: 
Ejecución assembly UsoStopwatch.exe
Animación 1. Ejecución assembly UsoStopwatch.exe.

4. Práctica: Código C#

En este ejemplo práctico (adaptación de [1]) definiremos dos métodos:
  • UsoThreads, y 
  • UsoPoolThreads.
Mediremos el tiempo de ejecución de cada uno con una instancia de la clase Stopwatch.

Descripción método UsoThreads (líneas 62-83):
  • numeroOperaciones: parámetro de número de operaciones asincrónicas a ejecutar.
  • Línea 64: Creación de objeto CountdownEvent como contador de operaciones ejecutadas.
  • Líneas 68-78: Ciclo for para la creación de objetos Thread al que pasamos como argumento una expresión lambda para mostrar el ID del thread y simular una espera de un milisegundo.
    • Línea 74: Indica que el thread ya terminó su ejecución.
    • Línea 77: Inicia la ejecución del thread recién creado.
  • Línea 80: Espera a que todos los threads hayan finalizado su ejecución.
Respecto al método UsoPoolThreads (líneas 85-105):
  • numeroOperaciones: Parámetro de número de operaciones asincrónicas a ejecutar.
  • Línea 87: Creación de objeto CountdownEvent como contador de operaciones ejecutadas.
  • Líneas 91-100: Ciclo for para la ejecución de cada una de las operaciones asincrónicas.
    • Línea 93: Invocación de Thread.QueueUserWorkItem con expresión lambda.
      • Línea 94: Muestra mensaje con el ID del thread en ejecución.
      • Línea 96: Tiempo de espera simulado de 100 milisegundos.
      • Línea 98: Indica que el thread ya terminó su ejecución.
  • Línea 102: Espera a que finalicen todas las operaciones asincrónicas.
Mientras que en Main (líneas 11-60): 
  • Línea 16: Número de operaciones a ejecutar en los dos modos (threads vs pool de threads).
  • Línea 19: Creación de cronómetro.
  • Líneas 23-25: Se mide el tiempo de ejecución en el modo threads.
  • Línea 36: Muestra el tiempo sincronizado para el modo threads.
  • Línea 41: Reestablece el cronómetro para medir el modo pool de threads.
  • Líneas 45-47: Se mide el tiempo de ejecución en el modo pool de threads.
  • Línea 57: Muestra el tiempo cronometrizado para el pool de threads.
Compilación: 

  1. csc /target:exe ParalelismoPoolThreads.cs

Ejecución assembly:

  1. .\ParalelismoPoolThreads.exe

> Prueba de ejecución: 
Ejecución assembly ParalelismoPoolThreads.exe
Animación 2. Ejecución assembly ParalelismoPoolThreads.exe.
Observamos en la Animación 2 que el tiempo requerido para crear/ejecutar threads es de apenas 00:00:00.16 (160 ms), mientras que para el pool de threads 00:00:07.59 (7s y 590 ms), pero nos enfrentamos a un trade-off:
  • El primer enfoque (threads) toma menos tiempo, pero es más costoso a nivel de recurso de máquina.
  • El pool de threads puede requerir más tiempo, pero hace uso efectivo de los recursos, pues reutiliza los threads en el pool de threads.

5. Conclusiones

Hemos hecho un paralelo entre el uso de threads y un pool de threads. Vimos que el modo threads es mucho más rápido pero más costoso (en términos de espacio de memoria y tiempo de procesador); mientras, que el modo pool de threads para la ejecución de operaciones asincrónicas es mucho más lento, pero mucho más efectivo en el uso de los recursos de la máquina. En situaciones como estas debemos hacer una evaluación concienzuda sobre cuál enfoque utilizar cuando nos enfrentemos al diseño de aplicaciones con operaciones asincrónicas.

6. Glosario

  • Asincronía
  • Ciclo
  • Clase
  • Cronómetro
  • Medición
  • Memoria
  • Recursos
  • Rendimiento
  • Sincronía

7. Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: Receta Multithreading en C# No. 2-6: Uso de la Clase CountdownEvent - http://ortizol.blogspot.com/2015/07/receta-multithreading-en-csharp-no-2-6-uso-de-la-clase-countdownevent.html
[3]: Receta Multithreading en C# No. 3-2: Exponer una Operación Asincrónica en un Pool de Threads - http://ortizol.blogspot.com/2015/07/receta-multithreading-en-csharp-no-3-2-exponer-una-operacion-asincronica-en-un-pool-de-threads.html
[4]: Stopwatch Class (System.Diagnostics) - https://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch(v=vs.110).aspx


V

No hay comentarios:

Publicar un comentario

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