Mostrando entradas con la etiqueta Sincronización. Mostrar todas las entradas
Mostrando entradas con la etiqueta Sincronización. Mostrar todas las entradas

jueves, 1 de marzo de 2018

Receta Multithreading en C# No. 6-1: Usar la Estructura de Datos Concurrente ConcurrentDictionary

1. Problema

Usar la estructura de datos basada en clave/valor ConcurrentDictionary.

2. Solución

ConcurrentDictionary es una estructura de datos -tipo diccionario- con capacidad de control de acceso -thread-safe-. Para las operaciones de lectura no es necesario bloquear la estructura, sin embargo para escribir nuevos elementos sí (Agafonov, 2013).


Vale agregar que las operaciones Count, IsEmpty, Keys, Values, CopyTo y ToArray requieren bloquear el acceso total a la estructura para poder ser efectuadas. De aquí se recomienda no ser usadas frecuentamente, pues el desempeño se verá significativamente afectado (Aganofov).

3. Código C#



Prueba de ejecución:
Tiempo ejecución lectura y escritura de Dictionary y ConcurrentDictionary
Figura 1. Tiempo ejecución lectura y escritura de Dictionary y ConcurrentDictionary.

En la Figura 1 se muestra que el desempeño de escritura de un objeto Dictionary es mucho mejor que un objeto ConcurrentDictionary. Los resultados son mejor en lectura por parte del ConcurrentDictionary, pero no llega a estar muy por encima de Dictionary.


Aunque la prueba anterior no es definitiva respecto al desempeño, sí que nos puede dar una perspectiva esencial para determinar cuál estructura de datos escoger para el tipo de problema que estamos resolviendo.

4. Literatura & Enlaces

Agafonov, E. Multithreading in C# 5.0 Cookbook. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.

O

sábado, 24 de febrero de 2018

Receta Multithreading en C# No. 5-9: Usar Tipos Dinámicos con el Operador await

1. Problema

Usar el operadir await con un tipo dinámico de C#.

2. Solución

Para esta receta se usa el paquete ImpromptuInterface: manejo de tipos dinámicos para la versión 4.0 o superiores de C#. Este paquete se agrega desde el administrador de paquetes NuGet (Agafonov, 2013).

3. Código C#

Archivo C# Programa.cs [enlace alternativo]:


Prueba de ejecución:

La tarea se está ejecutando en el thread con ID: 1. ¿Thread en el pool de threads?: true 
La tarea se está ejecutando en el thread con ID: 2. ¿Thread en el pool de threads?: true 

Repositorio: https://github.com/Fhernd/Recetas.Multithreading.CSharp

4. Literatura & Enlaces

Agafonov, E. Multithreading in C# 5.0 Cookbook. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
NuGet Gallery | ImpromptuInterface 6.2.2 (2018). Recuperado desde: https://www.nuget.org/packages/ImpromptuInterface/
O

miércoles, 13 de julio de 2016

Receta Multithreading en C# No. 5-8: Diseño Tipo Awaitable Personalizado

Índice

1. Introducción
2. Palabras Clave
3. Problema
4. Solución
5. Discusión de la Solución
5.1 Especificación de C# 5.0 para tipos awaitable personalizados
5.2 Interfaz InotifyCompletion
Conclusiones
Literatura & Enlaces

1. Introducción

Con esta receta C# se desea ilustrar cómo crear un tipo awaitable personalizado compatible con el operador asincrónico await. Más que de una aplicación concreta, el propósito es ilustrar en sentido académico cómo el programador puede crear una implementación personalizada de un tipo awaitable. Se describen los requerimientos para la definición de un tipo awaitable personalizado siguiendo la especificación del parágrafo 7.7.7.1 de la versión de C# 5.0. La sección práctica recurre al uso de la interfaz INotifyCompletion para la calendarización de continuaciones cuando el tipo await personalizado haya finalizado.

2. Palabras Clave

  • Await
  • Continuación
  • Interfaz
  • INotifyCompletion

3. Problema

Crear una implementación personalizada de un awaitable compatible con la función asincrónica await.

4. Solución

En .NET Framework se cuenta con la interfaz INotifyCompletion para el diseño de un tipo awaitable personalizado.

5. Discusión de la Solución

5.1 Especificación de C# 5.0 para tipos awaitable personalizados

En el parágrafo 7.7.7.1 (Agafonov, 2013) se definen los requerimientos para la definición de expresiones awaitable: 

Una expresión t es awaitable si se respeta cualquiera de estas reglas: 
  • La expresión t es un tipo de dato dynamic en tiempo de compilación. 
  • La expresión t tiene un método de instancia o método de extensión llamado GetAwaiter() sin parámetros regulares o tipos paramétricos y un tipo de retorno X que debe cumplir estas especificaciones: 
    • X debe implementar la interfaz System.Runtime.CompilerServices.INotifyCompletion (INotifyCompletion Interface", 2016).
    • X cuenta con una propiedad de sólo lectura pública de tipo bool llamada IsCompleted.
    • X cuenta un método de instancia público sin parámetros regulares ni paramétricos llamado GetResult().
Esta descripción está basada en la lectura del documento de especificación del lenguaje de programación C# que el lector puede encontrar en este enlace: CSharp 5.0 Language Specification.pdf.

Esta especificación se sigue en la sección práctica de esta receta para el diseño de un tipo awaitable personalizado.

5.2 Interfaz INotifyCompletion

La interfaz INotifyCompletion (namespace System.Runtime.CompilerServices) representa una operación que programa continuaciones una vez que finaliza su ejecución.

6. Práctica: Diseño de Tipo Awaitable Personalizado

Esta receta sigue el parágrafo 7.7.7.1 de la especificación de C# 5.0.

En primer lugar se implementa el tipo awaitable personalizado: 

La clase AwaitablePersonalizado (líneas 6-31) representa la implementación del tipo awaitable personalizado. De acuerdo a la especificación de C# 5.0, esta clase escribe el método GetAwaiter() (líneas 27-30); además retorna el tipo que implementa la interfaz INotifyCompletion: AwaiterPersonalizado.




Continuando, la clase AwaiterPersonalizado (líneas 11-77) implementa la interfaz INotifyCompletion. Estas son las características de esta clase: 
  • Líneas 25-28: Propiedad pública de sólo lectura IsCompleted para determinar si la ejecución de la operación ha finalizado.
  • Líneas 43-46: Método GetResult() que retorna el resultado de la operación.
Por último con la clase PersonalizacionAwait prueba el tipo awaitable personalizado: 

Archivo C# PersonalizacionAwait.cs [Enlace alternativo][Enlace alternativo]: 

En las líneas 24-33 se llevan a cabo estas operaciones: 
  • Línea 26: Se crea un objeto del tipo awaitable personalizado para ejecución sincrónica.
  • Línea 27: Se usa el operador await sobre el tipo awaitable personalizado.
  • Línea 28: Se muestra en la salida estándar el resultado de la ejecución sincrónica.
  • Línea 30: Creación de objeto de tipo AwaitablePersonalizado para ejecución asincrónica.
  • Línea 31: Uso operación asincrónica await para invocación asincrónica.
  • Línea 32: Muestra en la salida estándar el resultado de la operación asincrónica.
Prueba de ejecución: 

7. Conclusiones

Se ha demostrado como en C# es posible crear un tipo awaitable personalizado para operar con la función asincrónica await. Este conocimiento debe alentar al programador a incursionar o sentir curiosidad por temas avanzados que pueden tratarse con C#.


La última receta multithreading de esta serie Usando C# 5.0 enseña al programador a usar el operador asincrónico await con el tipo dynamic.

8. Literatura & Enlaces

Agafonov, E. (2013). Multithreading in C# 5.0 Cookbook. United States: Packt Publishing.
INotifyCompletion Interface (System.Runtime.CompilerServices) (2016, julio 13). Recuperado desde https://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.inotifycompletion(v=vs.110).aspx


O

martes, 12 de julio de 2016

Receta Multithreading en C# No. 5-7: Gestión Irregularidades de Tipo de Retorno void en Métodos Asincrónicos

Índice

1. Introducción
2. Palabras Clave
3. Problema
4. Solución
5. Discusión de la Solución
5.1 Tipo de retorno void
6. Práctica: Gestión de Tipo de Retorno void en Métodos Asincrónicas
7. Conclusiones
8. Literatura & Enlaces

1. Introducción

Llega la oportunidad de explorar cómo en un método asincrónico con tipo de retorno void se comporta el flujo de ejecución de una aplicación con múltiples tareas en ejecución. El propósito principal es conocer los problemas que compromete usar este tipo de retorno en la gestión de excepciones por parte del programador. En la sección práctica se demuestra los casos irregulares en la generación de excepciones en dos situaciones: bloque try-catch y una expresión lambda.

2. Palabras Clave

  • Asincronismo
  • Excepción
  • Expresión lambda
  • Operación asincrónica
  • Pool de threads
  • Thread
  • try-catch

3. Problema

Gestionar el tipo de retorno void en operaciones asincrónicas.

4. Solución

Aunque existen soluciones para gestionar el tipo de retorno void, en esta receta se describe cuáles son los problemas que generar este tipo de retorno en operaciones asincrónicas.

5. Discusión de la Solución

5.1 Tipo de retorno void

El tipo de retorno void especifica que un método no retorna ningún valor una vez ejecutada las operaciones de su implementación. Esto no tiene ningún problema en métodos sincrónicos. Sin embargo, en operaciones asincrónicas y más en particular en métodos que pueden generar excepciones sí que pueden producirse comportamientos anómalos e irregulares.

Continuando, esto es así porque en el contexto de sincronización la gestión de excepciones se lleva a cabo en el pool de threads; entonces, si una excepción no es gestionada, el proceso general terminará de forma abrupta (Agafonov, 2016).

Aún así, existen mecanismos para gestionar las excepciones como el evento AppDomain.UnhandledException. Este evento ocurre cuando una excepción no es capturada/gestionada ("AppDomain.UnhandledException Event", 2016).

Este es un ejemplo de uso de este evento: 

Archivo C# UsoUnhandledException.cs [Enlace alternativo][Enlace alternativo]: 

6. Práctica: Gestión de Tipo de Retorno void en Métodos Asincrónicos

El siguiente ejemplo adaptado de Agafonov (2013) demuestra cuáles son los problemas que pueden emerger en el uso del tipo de retorno void en una operación asincrónica.

La primera parte empieza con la descripción del método AsyncRetornoTaskConErrores() (líneas 64-69): este método retorna un objeto Task y genera una excepción simulada por medio de una cadena de texto.


Por otra parte, con AsyncRetornoVoidConErrores() (líneas 75-80) se simula también una excepción con una cadena de texto que incluye la palabra Exception.


El propósito con estos dos métodos es demostrar lo que ocurre al invocar una operación asincrónica que produce una excepción.


Como contraste, los métodos AsyncRetornoTask() (líneas 86-91) y AsyncRetornoVoid() (líneas 97-102) no generan ninguna excepción; sin embargo, en el caso de AsyncRetornoVoid() permite hacer la demostración de lo que ocurre con el tipo de retorno void. Mientras que AsyncRetornoTask() facilita la monitorización del estado de ejecución de la tarea asincrónica, gracias al tipo de retorno, por su parte AsyncRetornoVoid() no; para lograrlo se debe esperar un tiempo debido a que no cuenta con mecanismo de monitoreo de la operación asincrónica.


En cuanto al método Ejecutar() (líneas 15-58), estas son las operaciones que se ejecutan: 
  • Líneas 17-18: Definición de un objeto Task que recibe como retorno el objeto Task de AsyncRetornoTask(). Con t.Wait() se espera unos instantes mientras que la tarea asincrónica se completa.
  • Líneas 20-21: Invocación de AsyncRetornoVoid(). El monitoreo del estado de la operación asincrónica sólo se logra con un tiempo de espera. Este tiempo de espera es superior al tiempo ejecución simulado por el método. ¿La razón? El tipo de retorno de la operación asincrónica: void.
  • Líneas 23-29: Invocación de AsyncRetornoTaskConErrores(). El ciclo while comprueba que mientras no se genere ninguna falla se simula un tiempo de espera. Al final, línea 27, se muestra la excepción que se produjo con la invocación de esta método.
  • Líneas 31-55: Sirve al lector como comprobación del comportamiento irregular del manejo de excepciones de operaciones asincrónicas que retornan el tipo void. El lector puede tomar este código y verificar por medios propios lo que ocurre cuando se remueven los comentarios.
Esta es la demostración del código en ejecución: 

7. Conclusiones

Quedó demostrado cómo gestionar el tipo de retorno void de métodos asincrónicos. El no reconocer las problemas en manejo de excepciones en métodos con esta clase de firma puede hacer que un programa genere un comportamiento irregular y afecte la experiencia del usuario.

La próxima receta multithreading explicará cómo diseñar un tipo personalizado de await.

8. Literatura & Enlaces

Agafonov, E. (2013). Multithreading in C# 5.0 Cookbook. United States: Packt Publishing.
AppDomain.UnhandledException Event (System) (2016, julio 12). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception(v=vs.110).aspx


O

lunes, 11 de julio de 2016

Receta Multithreading en C# No. 5-6: Cambiar el Modo de Ejecución Asincrónica

Índice

1. Introducción
2. Palabras Clave
3. Problema
4. Solución
5. Discusión de la Solución
5.1 Método Task.ConfigureAwait()
6. Práctica: Cambio de Ejecución Asincrónico
7. Conclusiones
8. Literatura & Enlaces

1. Introducción

El modo de operación de la invocación de una función asincrónica await puede ser alterado, por ejemplo, en la ejecución de operaciones asincrónicas de una interfaz gráfica de usuario. Esta receta multithreading en C# explica cómo usar el método ConfigureAwait() para configurar la ejecución asincrónica de código que no accede a los componentes de la interfaz gráfica de usuario. La demostración en la sección práctica enseña el uso de este método alternando entre el uso del contexto de sincronización y sin recurrir a él.

2. Palabras Clave

  • Asincronismo
  • Contexto de sincronización
  • Interfaz gráfica de usuario
  • GUI
  • Sincronismo

3. Problema

Configurar el modo de ejecución asincrónico de una tarea.

4. Solución

El método ConfigureAwait() permite la configuración del modo de ejecución asincrónico.

5. Discusión de la Solución

5.1 Método Task.ConfigureAwait()

El método ConfigureAwait() configura el modo de ejecución asincrónica de una tarea. Este método recibe como argumento un valor booleano que en caso de ser true, el modo de ejecución asincrónica captura el contexto desde donde es ejecutada la tarea; en caso contrario la tarea se debe ejecutar sin capturar el contexto de sincronización.

Esta es su firma: 

public ConfiguredTaskAwaitable ConfigureAwait(
bool continueOnCapturedContext
)

6. Práctica: Cambio de Ejecución Asincrónica

La siguiente receta C# explica cómo el programador puede cambiar el modo de ejecución asincrónica.

El método que controla el evento Click (líneas 48-69) del botón que activa las operaciones asincrónicas lleva a cabo estas operaciones: 
  • Línea 51: Asigna el texto Calculando... cuando se inicia la ejecución asincrónica de dos tareas que operan en asincronismo con y sin contexto.
  • Línea 54: Inicia la ejecución de una operación con contexto.
  • Línea 55: Inicia la ejecución de una operación sin contexto.
  • Líneas 61-65: Construcción de la cadena de resultados de las dos operaciones asincrónicas anteriores.
  • Línea 68: Asigna el nuevo texto a la etiqueta de la interfaz gráfica de usuario.
Por otra parte, con el método OperacionContexto() (líneas 75-90) realiza estas operaciones: 
  • Línea 77: Especifica el número de iteraciones que ejecuta este método asincrónico.
  • Líneas 78-79: Creación e inicio de cronómetro.
  • Líneas 81-85: Repite la ejecución de una tarea asincrónica simulada. Aquí la tarea se ejecuta capturando implícitamente el contexto de ejecución.
  • Línea 87: Se detiene el cronómetro.
  • Línea 89: Retorna el tiempo que tomó la ejecución.
De manera análoga, el método OperacionSinContexto() (líneas 96-112) ejecuta estas operaciones: 
  • Línea 98: Especifica el número de iteraciones que ejecuta este método asincrónico.
  • Líneas 99-100: Creación e inicio de cronómetro.
  • Líneas 102-107: Repite la ejecución de una tarea asincrónica simulada. A través del método ConfigureAwait() se establece que el método se debe ejecutar sin capturar el contexto de sincronismo.
  • Línea 109: Se detiene el cronómetro.
  • Línea 111: Retorna el tiempo que tomó la ejecución.
Finalmente, con el método Ejecutar() (líneas 19-41) se construye la interfaz gráfica de una aplicación Windows Presentation Foundation (WPF).

Prueba de ejecución: 

7. Conclusiones

Se ha demostrado cómo a través del método ConfigureAwait() se puede alterar el modo de ejecución de una operación asincrónica: con y sin captura de contexto de sincronismo. Quedó claro que cuando no se captura el contexto el desempeño general de la aplicación es superior comparado con la captura del contexto.

Hasta ahora se ha estudiado los métodos asincrónicos que retornan un tipo paramétrico para el tipo Task, sin embargo en la siguiente receta multithreading se estudiará cómo trabajar con operaciones asincrónicas que retornan void.

8. Literatura & Enlaces

Agafonov, E. (2013). Multithreading in C# 5.0 Cookbook. United States: Packt Publishing.
Task.ConfigureAwait Method (Boolean) (System.Threading.Tasks) (2016, julio 11). Recuperado desde: https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.configureawait(v=vs.110).aspx


V

viernes, 25 de marzo de 2016

Receta Multithreading en C# No. 4-2: Cómo Realizar Operaciones Asincrónicas Básicas con una Tarea

Índice

1. Introducción
2. Palabras Clave
3. Problema
4. Solución
5. Discusión de la Solución
5.1 Diferencia entre la ejecución de una tarea en un pool de threads y en el thread de Main
6. Práctica: Operaciones Asincrónicas y Sincrónicas con Task
7. Conclusiones
8. Literatura & Enlaces

1. Introducción

En la primera receta multithreading -de la serie librería Task Parallel Library-, se estudió las generalidades de la clase Task para la ejecución de operaciones asincrónicas. Ahora, en esta nueva receta se comprende con más detalle cómo realizar operaciones asincrónicas con una tarea; en particular, se muestra cómo obtener el valor de retorno de la ejecución de una tarea asincrónica con un objeto de tipo Task.

2. Palabras Clave

  • Asincronismo
  • Pool de threads
  • Sincronismo
  • Tarea
  • Thread

3. Problema

¿Cómo obtener el resultado de la ejecución de una operación asincrónica usando una tarea?

4. Solución

La clase Task cuenta con una propiedad para la obtención del resultado de la ejecución de una operación asincrónica: Task.Result.

5. Discusión de la Solución

5.1 Diferencia entre la ejecución de una tarea en un pool de threads y en el thread de Main

La invocación de métodos directamente desde el punto de entrada de la aplicación -Main-, se lleva a cabo de forma sincrónica: Main continua su ciclo de ejecución una vez que la invocación de cualquier método haya finalizado.

Por el contrario, la ejecución de métodos asincrónicos es posible gracias al mecanismo de paralelismo que provee la clase Task ("Task Class", 2016). Esta característica es la que permite la ejecución simultánea de varias tareas sobre una misma aplicación. (Esta propiedad potencializa la creación de aplicaciones software más robustas y eficientes en términos de uso de recursos del sistema.)

Sin embargo, es importante mencionar que la clase Task cuenta con el método RunAsynchronously ("TaskRunAsynchronously", 2016) para permitir la ejecución sincrónica de una tarea. El control de ejecución en este caso pasa al thread desde donde se invoca este último método; por ejemplo, a Main. Esta medida de ejecución evita, entonces, la ejecución asincrónica en un pool de threads ("Task Class", 2016).

Con la instrucción de la línea 11 se muestra mensaje del ID de thread sobre el que se está ejecutando el método Main.

Más adelante, en la líneas 14-29 se ejecuta modo asincrónico el cálculo de la suma de los números entre 1 y 1000000. De forma análoga, la tarea que se define en las líneas 31-46 realiza el mismo cómputo. Sin embargo, esta última se ejecutará de forma sincrónica con la invocación del método RunAsynchronously (línea 49). Al final de esta implimentación de ejemplo, se muestra los resultados de la suma en cuestión.

Compilación:

csc /t:exe PoolThreadsVsMainThread.cs

Ejecución assembly:

.\PoolThreadsVsMainThread.exe

Demostración ejecución assembly (ideone.com): http://ideone.com/V2HNtn


Demostración ejecución assembly (local):
Ejecución assembly PoolThreadsVsMainThread.exe
Figura 1. Ejecución assembly PoolThreadsVsMainThread.exe.

6. Práctica: Operaciones Asincrónicas y Sincrónicas con Task

En este ejemplo se demuestra (una vez más: esto con el propósito de afianzar el conocimiento teórico y práctico que pretende proporcionar esta receta) el uso de varios elementos de la clase Task para la ejecución de operaciones sincrónicas y asincrónicas.

En la línea 12 se invoca al método OperacionTask de forma sincrónica: directamente desde el método Main. En las líneas 15-18 ocurre lo siguiente:
  • Línea 15: creación nuevo objeto Task con el tipo paramétrico int.
  • Línea 16: inicio de la ejecución asincrónica del método OperacionTask.
  • Línea 17: obtención del resultado de la ejecución de OperacionTask.
  • Línea 18: visualización resultado obtenido.
De manera análoga, la ejecución sincrónica de OperacionTask realiza estas tareas anteriores; sin embargo, se usa la invocación de RunAsynchronously (línea 22) para hacer que el método en cuestión se ejecute de modo sincrónico. (Nota: el método OperacionTask no se ejecuta en el pool de threads.)

Luego, en la línea 27 se crea un nuevo objeto Task para ejecución asincrónica de OperacionTask. A este objeto se le computa su estado de completitud asincrónico con la propiedad IsCompleted (línea 32). Por cada ciclo del bloque while (líneas 32-36) se obtiene el estado actual de ejecución con la propiedad Status.

Finalmente, en las líneas 40-42, se computa:
  • el estado de ejecución del último objeto Task, 
  • el resultado obtenido de la ejecución de OperacionTask, y 
  • y la visualización del resultado anterior.
Compilación: 

csc /t:exe SincronismoAsincronismo.cs

Ejecución assembly:

.\SincronismoAsincronismo.exe

Demostración ejecución assembly (ideone.com): http://ideone.com/2a3Mxp

Demostración ejecución assembly (local): 
Ejecución assembly SincronismoAsincronismo.exe
Figura 2. Ejecución assembly SincronismoAsincronismo.exe.

7. Conclusiones

Se comprendió cómo es posible la ejecución de operaciones asincrónicas básicas usando la clase Task. Esta misma clase también provee el método RunAsynchronously para la ejecución de operaciones sincrónicas. En la sección práctica se demostró la diferencia entre ejecutar una operación asincrónica en el pool de threads y la ejecución de una operación sincrónica en el thread de Main.

En la siguiente receta multithreading se demuestra cómo combinar dos o más tareas.

8. Literatura & Enlaces

Agafonov, E. (2013). Multithreading in C# 5.0 Cookbook. United States: Packt Publishing.
Task Class (System.Threading.Tasks) (2016, marzo 25). Recuperado desde:
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task(v=vs.110).aspx
Task.RunSynchronously Method (System.Threading.Tasks) (2016, marzo 25). Recuperado desde: https://msdn.microsoft.com/en-us/library/dd321435(v=vs.110).aspx


V