Receta Multithreading en C# No. 1-11: Manejo de Excepciones en Threads

Índice

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

0. Introducción

En el estudio de esta receta multithreading comprenderemos el proceso de manejo de excepciones de threads en ejecución. Comprenderemos que esta tarea compromete sólo el manejo de las excepciones al interior del método adyacente al thread. En el ejemplo de la sección práctica simularemos la generación de una excepción que no cuenta con el correspondiente bloque try-catch, y otro método en donde el control de la excepción se lleva a cabo correctamente. Veremos que sucede con ambas situaciones.

1. Problema

Estudiar el manejo de las excepciones generadas en el interior de la implementación de un proceso asociado o adyacente a un thread.

2. Solución

En el cuerpo de la implementación de un método, como tradicionalmente lo hacemos, podemos especificar código de manejo de excepciones con las construcciones sintácticas C# diseñadas para este cometido: try-catch-finally.

3. Discusión de la Solución

En artículos C# ya hemos tratado en cierto grado de profundidad el manejo de excepciones. Aquí están los enlaces a los cinco artículos integrales de esta serie de excepciones:
[Nota: Hago la inclusión de estos enlaces para evitar extendernos en esta tema de las excepciones que en el contexto de esta receta se da por entendido.]

Por otro lado, y en particular, vale hacer mención de algunos aspectos importantes y cruciales en el manejo de excepciones con threads en ejecución. El primero de ellos es la advertencia encontrada en [1]:
«…It is very important to always place a try-catch block inside the thread because it is not possible to catch an exception outside a thread’s code.»
Básicamente, lo anterior se puede resumir en que una excepción generada por un thread debe ser manipulada en el cuerpo de implementación del propio método adyacente, de lo contrario resulta imposible su manejo (como ya veremos en el ejemplo de la sección 4).

También hay que mencionar aquellas excepciones no manejables generadas, por ejemplo, con la suspensión de la ejecución de un thread (cfr. Abortar un Thread en Ejecución) o por la descarga de un dominio de aplicación [7]
  • ThreadAbortException [9]: Esta excepción es lanzada cuando se aborta la ejecución de un thread.
  • AppDomainUnloadedException [10]: Esta excepción se genera sobre un thread que ha intentado descargar un dominio de excepción (cfr. Creación de un Dominio de Aplicación).
Además como agregan en [7]:
«If any of these exceptions are unhandled in threads created by the common language runtime, the exception terminates the thread, but the common language runtime does not allow the exception to proceed further.»

4. Práctica: Código C#

En el siguiente ejemplo trataremos una excepción generada desde un método que se ejecuta sobre un thread independiente. Inclusive, demostraremos la generación de una excepción en el interior de un thread que no cuenta con un bloque de manejo excepciones try-catch para ver y distinguir su efecto ante la carencia de esta construcción sintáctica.

En las líneas 39-44 declaramos el método ThreadSinBloqueTryCatch. Este método genera una excepción tipo Exception, pero debemos resaltar que como se mencionó en la sección 3, es requisito indispensable que el cuerpo de implementación de un método adyacente a un thread cuenta con su propio bloque try-catch. Más adelante, líneas 47-61, se define el método ThreadConBloqueTryCatch, que a diferencia del método anterior, este sí cuenta con un bloque try-catch para manejar las excepciones que su lógica de implementación pudieran generar en el propio thread.


Continuando, en el punto de entrada de ejecución, Main, (líneas 8-35) se crea un primer thread de ejemplo para demostrar las capacidades de manejo de excepción interna del método ThreadConBloqueTryCatch: líneas 15, 18, y 21. Por el contrario, la operación de ejecución del thread t2 (línea 29) genera un error fatal en la aplicación que conlleva a su terminación abrupta. Esto se debe a la carencia del bloque try-catch del método ThreadSinBloqueTryCatch.


Compilación:

  1. csc /target:exe ManejoExcepcionesThread.cs


Ejecución assembly:

  1. .\ManejoExcepcionesThread.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):

Ejecución assembly ManejoExcepcionesThread.exe
Figura 1. Ejecución assembly ManejoExcepcionesThread.exe.

5. Conclusiones

Trabajamos en la práctica para la comprensión del manejo de excepciones en threads, tema de alta importancia debido a que su entendimiento nos permite construir aplicaciones multithreading tolerante a fallas lo que conlleva a la construcción de software más robusto y de nivel de diseño industrial. Con esta receta terminamos la serie de recetas dedicadas a los temas esenciales de threads en C#; para la próxima serie (capítulo 2) nos concentraremos en la sincronización de threads a lo largo de 9 entregas de este tipo.

6. Glosario

  • CLR
  • Descarga
  • Dominio de aplicación
  • Excepción
  • Exception
  • Multithreading
  • Sincronización
  • Thread
  • Tolerante a fallas

7. Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: Excepciones en C# – Parte 1: Introducción a las Excepciones | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/06/excepciones-en-csharp-parte-1-introduccion-a-las-excepciones.html
[3]: OrtizOL – Experiencias Construcción Software (xCSw): Excepciones en C# – Parte 2: Uso de Excepciones – http://ortizol.blogspot.com/2014/07/excepciones-en-csharp-parte-2-uso-de-excepciones.html
[4]: Excepciones en C# – Parte 3: Diseño de Excepciones Personalizadas | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/07/excepciones-en-csharp-parte-3-diseno-de-excepciones-personalizadas.html
[5]: Excepciones en C# – Parte 4: Propiedades de la Clase Base System.Exception | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/07/excepciones-en-csharp-parte-4-propiedades-de-la-clase-base-system.exception.html
[6]: Excepciones en C# – Parte 5: Ejemplos de Excepciones Comunes | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/07/excepciones-en-csharp-parte-5-ejemplos-de-excepciones-comunes.html
[7]: Exceptions in Managed Threads – http://msdn.microsoft.com/en-us/library/ms228965(v=vs.110).aspx
[8]: Receta Multithreading en C# No. 1-4: Abortar un Thread en Ejecución | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/06/receta-multithreading-en-csharp-no-1-4-abortar-un-thread-en-ejecucion.html
[9]: ThreadAbortException Class (System.Threading) – http://msdn.microsoft.com/en-us/library/system.threading.threadabortexception(v=vs.110).aspx
[10]: AppDomainUnloadedException Class (System) – http://msdn.microsoft.com/en-us/library/system.appdomainunloadedexception(v=vs.110).aspx
[11]: Receta No. 3-1 en C#: Creación de un Dominio de Aplicación (Application Domain) | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/05/receta-no-3-1-en-c-creacion-de-un.html

J

Receta T-SQL No. 1-15: Orden de los Resultados a partir de una Columna No Seleccionada

Índice

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

0. Introducción

En la receta T-SQL inmediatamente anterior aprendimos a especificar el orden de los resultados a partir de una o varias de las columnas enumeradas en la cláusula SELECT; ahora veremos que no es mandatorio recurrir a una de las columnas presentes en la cláusula SELECT para ordenar los resultados generador por la consulta.

1. Problema

Requerimos ordenar los resultados de una consulta dada a partir de uno o varios de las columnas pertenecientes a una determinada tabla, pero que no necesariamente está(n) especificada(s) en la cláusula SELECT.

2. Solución

Sobre la cláusula ORDER BY también es posible especificar cualquiera de las columnas (campos o atributos) de la tabla (o tablas) sobre la que queremos realizar la consulta; siempre y cuando éstas existan.

3. Discusión de la Solución

Especificar una de las columnas existentes sobre la cláusula ORDER BY [2], se efectúa de forma natural y simple como las demás columnas enumeradas en la cláusula SELECT [3]. En el caso del siguiente ejemplo:

SELECT p.name
FROM Production.Product AS p
ORDER BY p.Color;

Notemos cómo referenciamos al atributo Color de la tabla Product (renombrada como p) sobre la cláusula ORDER BY. (Desde luego este proceso de ORDER BY no tiene nada nuevo respecto al tema estudiado y practicado en la receta T-SQL Especificación de Criterios de Búsqueda de Registros en una Tabla.)

Una vez ejecutemos esta consulta sobre el motor de base de datos de SQL Server, los resultados obtenidos serán (sólo los trece primeros):

Especificación orden con columna no seleccionada.
Figura 1. Especificación orden con columna no seleccionada.

Por otro lado, debemos tener en cuenta la advertencia de uso de columnas no enumeradas enunciada en [1]:

«One caveat when ordering by unselected columns is that ORDER BY items must appear in the SELECT list if SELECT DISTINCT is specified…»

4. Práctica: Código T-SQL

Creemos una consulta para recuperar aquellos productos cuyo nombre empieza con la palabra ‘Lock Washer%’, y además deben estar ordenados de forma descendente por el campo ModifiedDate:
SELECT ProductID AS ‘ID Producto’, Name AS ‘Nombre’
FROM Production.Product
WHERE Name LIKE ‘Lock Washer%’
ORDER BY ModifiedDate DESC;
Evidentemente, en la cláusula SELECT hemos omitido la enumeración de la columna ModifiedDate; sin embargo, esta ha sido especificada como columna de ordenamiento descendente en la cláusula ORDER BY.

En la Figura 2 se muestra el resultado de ejecución de esta consulta:
ORDER BY ModifiedDate
Figura 2. Ordenamiento por la columna ModifiedDate.

5. Conclusiones

Hemos aprendido algo útil para ordenar los resultados de una consula: especificación en ORDER BY de columna no enumerada en la clásula SELECT. Esto resulta útil cuando queremos prescindir de una o varias columnas en el resultado generado por la ejecución de una consulta. En la próxima receta comprenderemos y pondremos en práctica modos de ordenamiento forzados.

6. Glosario

  • Cláusula
  • Consulta
  • ORDER BY
  • SELECT
  • SQL Server
  • T-SQL
  • WHERE

7. Literatura & Enlaces

[1]: SQL Server 2012 T-SQL Recipes – A Problem-Solucion Approach by Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield. Copyright 2012 Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield, 978-1-4302-4200-0.
[2]: ORDER BY Clause (Transact-SQL) – http://msdn.microsoft.com/en-us/library/ms188385.aspx
[3]: SELECT (Transact-SQL) – http://msdn.microsoft.com/en-us/library/ms189499.aspx

S

XML con C# (C1A3): Validación de Documentos XML con Esquemas y Definición de Tipo de Documentos

Índice

0. Introducción
1. Validación de Documentos XML
2. Validación con DTD
3. Validación con Esquema
4. Conclusiones
5. Glosario
6. Literatura & Enlaces

0. Introducción

En el artículo XML Reglas de la Gramática de XML estudiamos la composición de documentos XML a partir del uso de reglas de la gramática general para la creación correcta de este tipo de documento. Vimos que a diferencia de otros lenguajes derivados de SGCML, como HTML, XML no permite la omisión de etiquetas. Este tipo de adherencia a una gramática nos asegura la correcta formación de documentos (o well formed). Además de esta operación, estudiaremos un mecanismo de validación de las partes integrales (elementos, atributos, por ejemplo) de un documento XML; precisamente eso es lo que haremos, a modo de introducción, a lo largo del desarrollo de este artículo.

1. Validación de Documentos XML

Si lo que queremos es crear un documento XML que además de corresponder con las reglas de la gramática de este lenguaje de marcado, entonces el siguiente paso, y quizás más imporante, es el de validar su estructura. La validación consiste en crear documentos que sigan las reglas de una estructura de datos (e.g., un cliente con sus atributos básicos: nombre, apellido, dirección de correo electrónico, dirección); esto va a permitir que diferentes usuarios del documento XML puedan coincidir con la misma estructura sin la omisión o adición de nuevos atributos no requeridos o simplemente no admitidos por la parte que impone las reglas de validación.

Validar un documento XML, inclusive, responde a preguntas como:
  • ¿Cuál estructura se debe seguir para componer un documento de determinado tipo?
  • ¿Cómo garantizo que los demás programadores o sistemas generadores de archivos XML sigan la estructura impuesta para un documento XML?
Sencillamente, el proceso de validación se realiza siguiendo una de estas dos soluciones, cada una con sus desventajas y ventajas (las cuales trataremos a fondo en futuras entregas):
  • Document Type Definition (DTD), y 
  • Esquemas de documentos XML

Proceso de validación de un documento XML
Figura 1. Proceso de validación de un documento XML.

2. Validación con DTD

De acuerdo con [2] DTD (Document Type Definition) comprende un conjunto de declaraciones de lenguaje de marcado para la definición de un tipo de documento basado en SGCML (XML, HTML). La definición comprende, como se ha mencionado, el tipo de documento, es decir, su estructura. Esta estructura nos dice cuales bloques de construcción (elementos, atributos, valores, &c.) estarán permitidos sobre un documento XML que pretendamos editar.

Continuando, las reglas de composición o de validación podrán estar embebidas dentro del mismo documento XML o en un archivo externo. Esta última modalidad facilita la administración, pues sigue el enfoque de división y conquista.

Ahora, en breve veremos un ejemplo de uso para validar la composición de un documento XML para la estructura de datos Empleado.

El siguiente paso, una vez definida la estructura para Empleado, es componer un documento XML que emplee esta estructura:


Archivo XML Empleado.xml [enlace alternativo]:

Notemos cómo en la línea 2 referenciamos al archivo EmpleadoDefinicion.dtd como fuente de las reglas de validación del documento XML actual.

Para validar este archivo XML utilizaré una herramienta online XML Validation [4]:

Demostración de validación con DTD.
Figura 2. Demostración de validación con DTD.


3. Validación con Esquema

A pesar de que DTD constituye un método de validación apropiado y útil existe una alternativa que como se menciona en [1] tiene mayores ventajas: Esquema XML (o en inglés, XML schema). Esta alternativa de validación consiste en una descripción de los tipos permitidos en un documento XML. Esta descripción incluye las restricciones de las estructuras de datos además del contenido de estas.

Además, como mencionan en [5], las restricciones son expresadas en términos de las reglas gramaticales (cfr. Reglas de la Gramática de XML), predicados booleanos (lógicos), tipos de datos permitidos y sus atributos, entre otras reglas de integridad referencial entre tipos. Veamos un ejemplo de definición de un archivo de esquema:

Aquí podemos destacar a simple vista que este archivo de definición para la validación de un documento XML que incluya tipos Empleado, resulta más natural e intuitivo (independiente de su extensión); pues sigue las convenciones de declaración de un archivo XML en lo que se refiere a sus reglas de gramática.


[Nota: En futuras entregas discutiremos las ventajas de este tipo de validación frente al uso de DTD.]

4. Conclusiones

Hemos estudiado los conceptos básicos de validación de documentos XML. A modo de introducción aprendimos que existen dos métodos de validación: DTD y XML schema. Mostramos ejemplos de uso de estos dos resaltando algunas diferencias superficiales (seguro en un futuro cercano profundizaremos en aspectos más complejos de las diferencias para resaltar las ventajas existentes entre estos dos métodos de validación). En el próximo artículo XML estudiáremos a modo de introducción el proceso de parsear (parsing) de un documento XML.

5. Glosario

  • DTD
  • Esquema
  • Estructura de datos
  • HTML
  • SGML
  • Validación
  • XML

6. Literatura & Enlaces

[1]: Beginning XML with C# 2008 From Novice to Professional by Bipin Joshi. Copyright 2008 Bipin Joshi, 978-1-4302-0998-0.
[2]: Document type definition – Wikipedia, the free encyclopedia – https://en.wikipedia.org/wiki/Document_Type_Definition
[3]: XML DTD – http://www.w3schools.com/xml/xml_dtd.asp
[4]: XML Validation: XML Validation – http://xmlvalidation.com/index.php?id=1&L=0
[5]: XML schema – Wikipedia, the free encyclopedia – https://en.wikipedia.org/wiki/XML_schema
[6]: XML con C# (C1A2): Reglas de la Gramática de XML | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/08/xml-con-csharp-c1a2-reglas-de-la-gramatica-de-xml.html

J

Receta Multithreading en C# No. 1-10: Bloqueo Región Crítica con un Monitor

Índice

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

0. Introducción

En recetas de la categoría C# ya hemos abordado el tema de sincronización de acceso concurrente a recursos compartidos por parte de múltiples threads; a diferencia de este caso, sobre esta receta multithreading vamos a tratar un caso particular en el que a través del uso de los miembros de la clase Monitor (System.Threading) podemos obtener el control de acceso a un recurso compartido, además de resaltar la situación particular en donde por razones lógicas en la implementación incorrecta se genera un bloqueo o deadlock sobre el recurso compartido.

1. Problema

Encontrar e implementar un método de bloqueo sincrónico para el control de acceso concurrente a un recurso compartido (e.g., archivo, conexión de red, modificación de datos en memoria).

2. Solución

La biblioteca base de clases de .NET cuentan con el mecanismo de control de acceso concurrente de múltiples threads por medio de la clase Monitor del nombre de espacio System.Threading.

3. Discusión de la Solución

La clase System.Threading.Monitor [2] provee el mecanismo de sincronización de acceso a recursos compartidos. Los miembros de esta clase permiten al programador tener mayor control de las áreas o regiones de código críticas para evitar intentos de modificación concurrentes por múltiples threads en ejecución que pudieran generar incoherencia en los resultados obtenidos o en el estado de un objeto en memoria.


Su uso comprende, en un principio, la siguiente sintaxis declarativa:


object locker = new object();

Monitor.Enter (locker);

try
{
// Conjunto de instrucciones sincrónicas
}
finally
{
Monitor.Exit (locker);
}

Aquí podemos destacar los siguientes elementos sintáticos:
  • locker: instancia de Object para mantener el bloqueo en una región de código crítica.
  • Monitor.Enter (locker): Invocación del método Enter [4] para iniciar el bloqueo sobre la región crítica.
  • Bloque try-finally: A pesar de que no es requisito indispensable crear este bloque, si que resulta recomendable para controlar cualquier excepción que se pudiera generar en el area de región crítica.
  • Monitor.Exit (locker): Liberación del recurso de la región crítica. Nótese que se debe pasar al método Exit [5] la instancia de bloqueo, locker en este caso.
Para adentrarnos en el uso básico de esta clase y sus miembros mencionados, recurramos a un ejemplo de uso:


Archivo C# AccesoArchivoConMonitor.cs [enlace alternativo]:

Con el ciclo for en las líneas 41-46 creamos hasta 5 threads que van intentar acceder al archivo (nombre-threads.txtpara agregar una línea de texto con su nombre (línea 44). Por cada iteración del ciclo se inicia la ejecución (línea 45) del thread recién instanciado. En la línea 9 creamos una instancia del objeto bloqueante para la sincronización que se efectúa dentro del método EscritirArchivo.


Dentro del método EscritirArchivo (líneas 11-37) se empieza con una línea que pausa el thread actual durante 1000 milisegundos (1 segundo) (línea 13). Obtenemos el nombre del thread en la línea 15. (Este nos va a servir para agregar la entrada de texto sobre el archivo.) En plena línea 20 invocamos al método Enter y le pasamos el objeto bloqueante declarado en la línea 9. (A partir de aquí empieza la sección crítica.) En el bloque try se abre/crea el archivo threads.txt con la clase StreamWriter [9] y se agrega el nombre y número de thread al archivo (línea 25).


Continuando, en el bloque finally se invoca al método Exit para desbloquear o finalizar la sección crítica y dar paso al siguiente thread localizado en la cola de espera.


Compilación:

  1. csc /target:exe AccesoArchivoConMonitor.cs


Ejecución assembly:

  1. .\AccesoArchivoConMonitor.exe


> Prueba de ejecución (local):

Ejecución assembly AccesoArchivoConMonitor.exe
Figura 1. Ejecución assembly AccesoArchivoConMonitor.exe.

Notemos que por cada ejecución el orden de ejecución varía, debido a la naturaleza estocástica de ejecución procesos paralelos.


Contenido del archivo threads.txt [enlace alternativo]:

[Nota: En la receta C# Sincronizar la Ejecución de Múltiples Threads usando un Monitor podrán encontrar una versión extendida de varios tópicos relacionados con el uso de la clase Monitor y varios ejemplos de uso. Recomendado.]

4. Práctica: Código C#

Adaptemos el ejemplo de uso de la clase Monitor y de la sentencia lock hallado en [1] para comprender y poner en práctica las situaciones en que se puede producir un bloqueo permanenente sobre una región crítica por un error semántico de parte del programador al intentar obtener el bloqueo sobre objetos ya en uso, y además, como vimos en la sección 3 recurrir al uso de los miembros de Monitor para hacer más natural el acceso sincrónico a un recurso compartido.

El método MetodoConBloqueoExtendido (líneas 57-66) simula un bloqueo de un región crítica durante un determinado tiempo. Por otro lado, en lo que se refiere a Main (líneas 12-54) ocurren las siguientes operaciones:
  • Línea 17: Creación de instancia de Thread para la creación de un thread que encapsula el método MetodoConBloqueoExtendido.
  • Línea 20: Inicio de la ejecución del método MetodoConBloqueoExtendido. En este caso se inicia un bloqueo sobre la instancia Object bloqueo1 a través de la referencia o1; se espera un segundo (1000 ms) y enseguida se genera un bloqueo con lock sobre la referencia o2, el cual se libera enseguida.
  • Línea 23: quasi paralelamente sobre el thread Main se inicia un bloqueo sobre el objeto bloqueo2, se espera un segundo (línea 25) y dentro de la sentencia if (línea 28) se intenta realizar un bloqueo en un tiempo de espera de 5 segundos. Esta espera permite que se libere el bloqueo realizado sobre el método MetodoConBloqueoExtendido.
También hay que resaltar estas otras operaciones:
  • Línea 40: Creación de nuevo thread que encapsula el método MetodoConBloqueoExtendido.
  • Línea 43: Inicio de ejecución del thread recién creado. El método MetodoConBloqueoExtendido inicia su ejecución.
  • Línea 45: Uso de lock con bloqueo2.
  • Línea 49: Aquí es donde se manifiesta el problema de bloqueo permanente, debido a que se intenta obtener el control sobre la región crítica con el objeto bloqueo1 que se encuentra ya bloqueado desde MetodoConBloqueoExtendido. (El término anglosajón para referirse a un bloqueo de este tipo es precisamente deadlock). Otra forma en la que nos podemos referir a esta situación es de competencia por acceso a un recurso compartido mutuamente excluyente.
Compilación:

  1. csc /target:exe BloqueoRegionCritica.cs

Ejecución assembly:

  1. .\BloqueoRegionCritica.exe


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

5. Artefactos

Enlaces de descarga de los artefactos producidos a lo largo de la realización de esta receta:

6. Conclusiones

En esta receta multithreading se demostró uno de los errores semánticos más comunes que se comenten cuando se intenta acceder a una región crítica que está siendo controlada por un objeto de bloqueo. La competencia por recursos (deadlockpuede llevar a esta situación. Es aquí donde el programador debe prestar cuidadosa atención sobre el uso de la construcción lock y los miembros de la clase Monitor cuando se intente obtener el control sobre una región crítica. En la siguiente receta multithreading (la última de la serie del capítulo de los esenciales de threads) estudiaremos el manejo de excepciones sobre threads.

7. Glosario

  • .NET
  • BCL
  • Biblioteca base de clases
  • Deadlock
  • Microsoft
  • Multithreading
  • Thread

8. Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: Monitor Class (System.Threading) – http://msdn.microsoft.com/en-us/library/System.Threading.Monitor(v=vs.110).aspx
[3]: Receta C# No. 4-7: Sincronizar la Ejecución de Múltiples Threads usando un Monitor | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2014/07/receta-csharp-no-4-7-sincronizar-la-ejecucion-de-multiples-threads-usando-un-monitor.html
[4]: Monitor.Enter Method (System.Threading) – http://msdn.microsoft.com/en-us/library/system.threading.monitor.enter(v=vs.110).aspx
[5]: Monitor.Exit Method (System.Threading) – http://msdn.microsoft.com/en-us/library/system.threading.monitor.exit(v=vs.110).aspx

S

Receta T-SQL No. 1-14: Especificación del Orden de los Resultados de una Consulta

Índice

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

0. Introducción

En la receta T-SQL anterior aprendimos cómo ordenar los resultados de una búsqueda especificando el nombre de una columna como expresión de orden. En esta receta agregaremos los conceptos de orden ascendente y descendente. Por ejemplo para valores numéricos podemos especificar de menor a mayor, o en sentido contrario, de mayor a menor. Veamos cómo se realiza esta tarea usando el lenguaje de consulta estructurado T-SQL.

1. Problema

Además de ordenar los resultados por grupo de columnas especifíficas, ahora es requisito sine qua non establecer en qué orden (i.e., mayor a menor, o menor a mayor) se deben, valga el pleonasmo, ordenar los resultados para ese conjunto de columnas.

2. Solución

Sobre la cláusula ORDER BY podemos especificar de forma explícita el modo de ordenamiento: ascencente o descente, a través de las palabras claves ASC y DESC, respectivamente.

3. Discusión de la Solución

La cláusula ORDER BY [2] ordena los resultados generados por una consulta. Su uso puede aplicarse en situaciones como:
  • Ordenamiento del resultado a partir de un listado de columnas. El orden se establece a partir de la primera columna, luego por la segunda, y así sucesivamente. (La omisión de la cláusula ORDER BY no garantiza un orden específico cada vez que ejecutemos una consulta, es por eso que debe ser explícita su inclusión al final de la consulta.)
  • Generación de un límite de filas en un orden específico.
Sintaxis T-SQL [2]:

ORDER BY order_by_expression
    [ ASC | DESC ] 
Descripción puntual:
  • order_by_expression: Grupo de columnas a partir de las cuales se realizará el orden de los resultados de la consulta. Los nombres de las columnas deben ser únicos y el tamaño total de las columnas no debe exceder los 8060 bytes.
  • ASC | DESC (opcional): Los valores de las columnas son ordenados ya sea en orden ascendente (ASC) o descedente (DESC).
[Nota: Esta descripción sintáctica de la cláusula ORDER BY comprende su uso básico, en [2] podrán encontrar su versión extendida.]

De manera predeterminada los resultados se orden ascendentemente (ya sean valores numéricos o alfabéticos). Para hacerlo explícito entonces utilizamos la palabra clave ASC por delante del nombre de la columna. Lo mismo hacemos para el orden inverso.

Veamos un ejemplo básico de uso de la cláusula ORDER BY:
Ordenemos por el ID de producto todos aquellos productos que su nombre empiece por la cadena de caracteres Lock Washer, además de que el ID del producto se se orde descendentemente:

SELECT ProductID AS ‘ID Producto’, Name AS ‘Nombre’
FROM Production.Product
WHERE Name LIKE ‘Lock Washer%’
ORDER BY ProductID DESC;

La ejecución de esta consulta sobre SQL Server 2014 Management Studio genera el siguiente resultado ordenado:
Orden descendente de resultados
Figura 1. Orden descendente de resultados de una búsqueda.


4. Práctica: Código T-SQL

Recurramos a un ejemplo más complejo para afianzar nuestro conocimiento sobre el uso de la cláusula ORDER BY:

SELECT p.Name AS ‘Nombre Producto’,
h.EndDate AS ‘Fecha’,
h.ListPrice AS ‘Precio de Lista’
FROM Production.Product AS p
INNER JOIN Production.ProductListPriceHistory AS h
ON p.ProductID = h.ProductID
ORDER BY p.Name DESC,
h.EndDate DESC;

En esta consulta queremos mostrar todos aquellos registros de las tablas Product y ProductListPriceHistory que coincidan con el ID de producto (p.ProductID = h.ProductID). (Más adelante comprenderemos el uso de INNER JOIN.) El resultado de esta consulta estará ordenado de forma descedente por el nombre del producto (p.Name), y por la fecha del historial de precios de producto (h.EndDate).

Probemos ejecutando esta consulta para obtener el resultado:
Orden descendente para las columnas Name y EndDate.
Figura 2. Orden descendente para las columnas Name y EndDate.

5. Conclusiones

Aprendimos cómo ordenar las columnas del resultado de una consulta especificando ya sea en orden ascendente (ASC) o descendente (DESC) en conjugación con la cláusula ORDER BY. En la siguiente receta estudiarémos cómo realizar el orden de columnas no enlistadas en la cláusula SELECT.

6. Glosario

  • ASC
  • Ascendente
  • Cláusula
  • Consulta
  • DESC
  • Descendente
  • Orden
  • SQL Server
  • T-SQL

7. Literatura & Enlaces

[1]: SQL Server 2012 T-SQL Recipes – A Problem-Solucion Approach by Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield. Copyright 2012 Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield, 978-1-4302-4200-0.
[2]: ORDER BY Clause (Transact-SQL) – http://msdn.microsoft.com/en-us/library/ms188385.aspx

S

Sintaxis y Semántica de un Iterador en C#

Índice

0. Introducción
1. ¿Qué es un Iterador?
2. Implementación Básica de un Iterador
3. Semántica Iteradores
3.1 Uso múltiple de sentencias yield return
3.2 Uso de yield return
3.2 Bloques try/catch/finally en iteradores
4. Ejemplo de un iterador
5. Artefactos
6. Conclusiones
7. Glosario
8. Literatura & Enlaces

0. Introducción

En el artículos anterior estudiamos los enumeradores: construcciones (enumerable y enumeradores) útiles para la iteración de estructuras de datos creados por los propios programadores o las existentes en la biblioteca base de clases de .NET. En esta oportunidad vamos a tratar otro tema relacionado: se trata de los iteradores. Este tipo de construcción facilita el recorrido por cada uno de los elementos de una colección de objetos. En esta apartado trataremos los siguientes temas: consumidor y productor de un enumerador, semántica de los iteradores, manejo de excepciones en un iterador, y el uso de la palabra clave yield en combinación con la instrucción de retorno de valor de método return.

1. ¿Qué es un Iterador?

Podríamos definir en términos simples y directos a un iterador como un objeto que recorre los elementos almacenados en una estructura de datos o una colección (e.g., un arreglo o una lista). Gráficamente lo podríamos ver de la siguiente manera:

Analogía de un iterador
Figura 1. Analogía de un iterador.

En la Figura 1 se muestra a un invididuo identificado como Iterador. Este individuo se encarga de explorar cada elemento (de izquiera a derecha) de la colección que aparece en la parte superior.

2. Implementación Básica de un Iterador

De acuerdo con [1] un identificamos dos elementos básicos para iterar una estructura de datos o colección:
  • Consumdor (consumer): A través de la sentencia foreach se recorre o se consume los elementos de la estructura o colección.
  • Productor (producer): Generador o productor del enumador (cfr. Enumeradores).
Para comprender mejor estos conceptos, adaptemos el ejemplo hallado en [1]:

En las líneas 22-37 declaramos el método Fibonacci que se encarga de generar la serie hasta la cantidad especificada por su parámetro numero. Por cada iteración del ciclo for (líneas 27-36) se genera un valor de la serie. Este valor se retorna al ciclo foreach (líneas 12-15). En la siguiente iteración el control de ejecución del método Fibonacci continua en la línea 32. Este ciclo termina hasta que se hayan generado, como en este ejemplo, los 7 primeros números de la serie Fibonacci.


Para extendernos un poco debemos resaltar dos elementos:

  • yield return: Una vez en un método de iteración de elementos se alcance la línea que contiene esta instrucción compuesta, la ubicación es recordada, y el valor de retorno es devuelto al iterador o consumidor.


    En cuanto a la instrucción
    return simple, la diferencia radica en que una vez se alcanza esta el flujo de control pasa al método que hizo la llamada del método. Sería algo así como [1]:

    Aquí está el valor que ha solicitado desde este método.

    Mientras que yield return:

    Aquí está el siguiente valor que ha solicitado desde este enumerador.”

  • IEnumerable<int>: Como el método Fibonacci se trata de un método iterador era necesario especificar como tipo de retorno la interfaz IEnumerable.
A este último punto vale agregar que el tipo de retorno ya sea para un método o método de acceso debe ser cualquier de estas interfaces con un tipo paramétrico específico (en el caso de las interfaces genéricas):
  • Interfaces Enumerable:
    • System.Collections.IEnumerable
    • System.Collections.Generic.IEnumerable
  • Interfaces Enumerator:
    • System.Collections.IEnumerator
    • System.Collections.Generic.IEnumerator
Internamente el compilador realiza una serie de manipulaciones para crear una clase privada que implementa los métodos MoveNext, Reset y la propiedad Current. La nota completa acerca de este tipo de inversión es explicado con mejor detalle en la Figura 2 [1]:


Tarea del compilador con los enumeradores
Figura 2. Tarea del compilador con los enumeradores.

Para finalizar con el ejemplo del archivo C# IteradorSerieFibonacci.cs realizamos estas tareas:

Compilación:

  1. csc /target:exe IteradorSerieFibonacci.cs

Ejecución assembly:

  1. .\IteradorSerieFibonacci.exe


> Prueba de ejecución (local):
Ejecución assembly IteradorSerieFibonacci.exe
Figura 3. Ejecución assembly IteradorSerieFibonacci.exe.

3. Semántica Iteradores

3.1 Uso múltiple de sentencias yield return

En la lógica de implementación de un método iterador podemos especificar múltiples instrucciones yield return:

A resaltar las líneas del método ListaNumeros por cada iteración realizada por el consumidor de la iteración (líneas 13-16), este método retorna valor por valor (1, 2, y 3) de cada setencia yield return.


Compilación:

  1. csc /target:exe Multiplesyield.cs


Ejecución assembly:

  1. .\Multiplesyield.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):

Ejecución assembly Multiplesyieldi.exe
Figura 4. Ejecución assembly Multiplesyieldi.exe.


3.2 Uso de yield break

Para finalizar arbitrariamente un método iterador o un propiedad de acceso de un iterador, podemos utilizar la sentencia compuesta yield break. Esto evita que las sentencias yield return de líneas posteriores no serán parte de la secuencia generada por el consumidor de la iteración. Así:
static IEnumerable<String> MetodoIterador(bool continuar)
{
yield return “Primero”;
yield return “Segundo”;

if (!continuar)
{
yield break;
}

yield return “Tercero”;
}
Asumiendo que el valor pasado como argumento para el parámetro bool sea false la sentencia if se evaluará como true y el método iterador MetodoIterador finaliza por completo sin alcanzar la última sentencia yield return.

3.3 Bloques try/catch/finally en iteradores

De acuerdo con [1] una sentencia compuesta yield return no puede ser parte de un bloque try-catch; como en este ejemplo:

static IEnumerable> MetodoIterador()
{
try
{
yield return 1;
}
catch
{
//…
}

yield return 2;
}
Al intentar compilar un archivo de código fuente C# que contenga esta implementación, se generará el error CS1626 [4]. Esta restricción se debe a que el compilador debe generar clases privadas que implementen los miembros MoveNext, Current, Reset, y Dispose. Sin embargo, sí que está permitido la combinación try/finally:

static IEnumerable> MetodoIterador()
{
try
{
yield return 1;
}
finally
{
//…
}
}

Para este caso, cuando el consumidor del iterador haya finalizado, el bloque de instrucciones en el bloque finally se ejecutarán. Para aquellos enumeradores que son finalizados antes del recorrido completo (como en la sección 3.1), el consumidor del iterador implícitamente se encarga de liberar los recursos utilizados.

4. Ejemplo de un Iterador

Creemos un ejemplo refleje la creación de un iterador simple para generar números pares a partir de un rango de números enteros.

En las líneas 22-32 el método iterador GeneradorNumerosPares genera un número par a partir del rango especificado por los valores pasados a los argumentos. Por cada iteración retorna un número par (la validación se realiza con la sentencia if de la línea 27).


Compilación:

  1. csc /target:exe IteradorNumerosEnteros.cs


Ejecución assembly:

  1. .\IteradorNumerosEnteros.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):

Ejecución assembly IteradorNumerosPares.exe
Figura 5. Ejecución assembly IteradorNumerosPares.exe.


5. Artefactos

Lista de enlaces de los artefactos producidos a lo largo del desarrollo de este artículo:

6. Conclusiones

Hemos comprendido una nueva construcción para la iteración de los elementos de una colección a partir del uso de las interfaces IEnumerable (genérica y no genérica) y IEnumerator (génerica y no genérica). La sentencia compuesta yield return es la encargada de retornar el valor de un elemento de una estructura o una colección por cada elemento explorado o iterado. En el próximo artículo nos centraremos la composición de secuencias; un tema muy similar al que que acabamos de estudiar, y por supuesto, de bastante utilidad.

7. Glosario

  • Bloque
  • Colección
  • Elemento
  • Estructura de datos
  • Excepción
  • Iterador
  • Semántica
  • Sintaxis

8. Literatura & Enlaces

[1]: C# 5.0 in a Nutshell by Joseph Albahari and Ben Albahari. Copyright 2012 Joseph Albahari and Ben Albahari, 978-1-449-32010-2.
[2]: Iterators (C# and Visual Basic) – http://msdn.microsoft.com/en-us/library/dscyy5s0.aspx
[3]: Enumeradores en C# | OrtizOL – Experiencias Construcción Software (xCSw)- http://ortizol.blogspot.com/2014/08/enumeradores-en-csharp.html
[4]: Compiler Error CS1626 – http://msdn.microsoft.com/en-us/library/cs1x15az(v=vs.90).aspx

J

Pregunta C# (4 de 20): ¿Cómo Obtener la Ubicación de una Aplicación de Consola en C#?

Índice

0. Introducción
1. Contexto
1.1 ¿Qué es una aplicación consola?
1.2 Aplicaciones consola en .NET
2. Obtención de la Ubicación de una Aplicación Consola en Ejecución
2.1 Clase System.Reflection.Assembly
2.2 Método Path.GetDirectoryName(string)
3. Aplicación
4. Recursos
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

Esta es la pregunta número 4 (de 20) del curso de Twenty C# Questions Explained de Microsoft Virtual Academy. En esta oportunidad vamos a conocer el proceso para la obtención de la ubicación en disco de un assembly que se haye en ejecución. Esto nos puede resultar útil, por ejemplo, para escribir o leer un archivo binario o de texto directamente desde esa ubicación como recurso de almacenamiento del estado de la aplicación o de la persistencia de los datos del usuario.

1. Contexto

1.1 ¿Qué es una aplicación consola?

Una aplicación consola consiste en un ambiente de trabajo basado en texto. Este ambiente está compuesto naturalmente por dos partes bien definidas:
  1. La entrada estándar: teclado
  2. La salida estándar: el monitor (o la pantalla)
La interacción entre el usuario y el sistema basado en consola se realiza a través de comandos textuales (uso de la entrada estándar). Estos comandos ejecutan operaciones y una vez finalizada su ejecución presentan (aunque no necesariamente) el resultado al usuario en la salida estándar.
Los sistema operativos modernos (e.g., Windows, MacOS, o aquellos basados en Unix) cuentan con este medio de interacción para controlar o llevar a cabo tareas administrativas o inclusive en la actualidad se siguen utilizando software de producción empresarial. En el caso del sistema operativo Windows, éste viene integrado con el programa COM.EXE; con este programa podemos indicar al sistema, por ejemplo, para la navegación entre directorios de usuario o programa, manipular archivos, ejecutar otros programas, etc. En la Figura 1 una captura de pantalla de la ejecución de COM.EXE.


CMD.EXE
Figura 1. CMD.EXE.

Para operaciones administrativas avanzadas del sistema operativo Windows, Microsoft tiene a disposición otro entorno similar al anterior pero aún más robusto y poderoso, debido a que se trata de un marco de trabajo para la automatización y configuración de tareas administrativas gracias a su propio lenguaje de scripting (o instrucciones diseñadas para tareas específicas) [3]: Windows PowerShell.


Windows PowerShell
Figura 2. Windows PowerShell.

1.2 Aplicaciones consola en .NET

Microsoft .NET Framework cuentan con los artefactos de software para la la creación, ejecución de aplicaciones basadas en consola. Estas aplicaciones las podemos crear directamente usando un editor de texto plano (e.g., Notepad++) o de manera más cómoda y ágil sobre el entorno de desarrollo integrado Visual Studio. (Muchos de los seguidores de este blog ya habrán visto que los ejemplos de ejecución se llevan a cabo en un ambiente como este, y que la escritura de los programas de demostración se escriben casi en su totalidad en Notepad++.)

Continuando, en el caso de Visual Studio la creación de una aplicación consola lo podemos hacer siguiendo estos pasos:
  1. Menú File
  2. Submenú New
  3. Comando Project…
  4. Sección Visual C#
  5. Sub-sección Windows
  6. Selección de la plantilla Console Application
  7. Asignamos un nombre y una ubicación al proyecto y a la solución
  8. Clic en OK
Creación aplicación consola en Visual Studio 2013
Figura 3. Creación aplicación consola en Visual Studio 2013.

2. Obtención de la Ubicación de una Aplicación de Consola en Ejecución

Ahora que hemos comprendido los esenciales de lo que se entiende por una aplicación consola, pasemos a explicar los artefactos necesarios para obtener la ubicación de una aplicación de este tipo, además de una demostración en código C# sobre los resultados prácticos de la implementación de estos.

2.1 Clase System.Reflection.Assembly

La clase Assembly [10] (N:System.Reflection) representa la construcción modular de un assembly (cfr. Creación y Uso de un Módulo) en el Framework .NET. Esta clase es capaz de describir los artefactos integrales de la biblioteca de clases del Framework, como también los propios creados por un programador. Además, ofrece miembros (métodos, precisamente) que permiten cargar un assembly en caliente (con esta expresión me refiero a la posibilidad de cargar nuevos artefactos del modelo, o componentes de terceros, una vez el proyecto haya sido construido (build time) el componente).


Lo que nos interesa en particular de esta clase es el método GetExecutingAssembly [6]. Con este método obtenemos el assembly que contiene la aplicación en ejecución. Esta es su firma:

public static Assembly GetExecutingAssembly()

Como mencionamos obtiene una instancia del assembly en ejecución, una vez obtenido podemos utilizar la propiedad Location [7] para la representación en cadena de caracteres de la dirección en disco en donde se haya el assembly:

public virtual string Location { get; }

2.2 Método Path.GetDirectoryName(string)

A continuación el artefacto que requerimos para mostrar la información de un directorio específico (el obtenido con la propiedad Location) es el método GetDirectoryName [8] (clase System.IO.Path). Su firma:

public static string GetDirectoryName(string path)

3. Aplicación

Ahora que ya contamos con los fundamentos requeridos, pasemos a construir un ejemplo muy práctico y sencillo sobre cómo mostrar en pantalla de la consola la ubicación actual de un assembly que crearemos a mano con el uso de Notepad++ como editor de código fuente C#.

En la línea 17 utilizamos la propiedad Location para obtener la ubicación en disco del assembly ObtencionUbicacionAssembly.exe, el cual se haya en ejecución. La representación de esta ubicación es una cadena de caracteres la cual la asignamos a la variable ubicacion (tipo string). Más adelante, línea 21-22, utilizamos el método GetDirectoryName al cual le pasamos como argumento la ubicación almacenada en la variable ubicacion:


Path.GetDirectoryName(ubicacion)

Este método retorna la representación string que al mismo tiempo sirve como argumento del método WriteLine, el se encarga de presentar en pantalla (salida estándar) del nombre del directorio donde se encuentra el assembly ObtencionUbicacionAssembly.exe.


Compilación:

  1. csc /target:exe ObtencionUbicacionAssembly.cs


Ejecución assembly:

  1. .\ObtencionUbicacionAssembly.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local):

Ejecución assembly ObtensionUbicacionAssembly.exe
Figura 4. Ejecución assembly ObtensionUbicacionAssembly.exe.


4. Recursos

El recuso multimedial presentando en el curso Twenty C# Questions Explained en Microsoft Virtual Academy:

5. Conclusiones

Hemos comprendido el proceso de obtención de la dirección en disco de un assembly en ejecución, el cual nos puede resultar muy útil cuando, por ejemplo, queremos realizar operaciones de manipulación de archivos de texto plano o binarios desde disco. Con las herramientas que nos ofrece el Framework .NET podemos realizar estas tareas de forma sencilla y directa (el ejemplo escrito en la sección 4 lo demuestra). En la próxima pregunta explicaremos cómo invocar el constructor de clase base.

6. Glosario

  • Assembly
  • Directorio
  • IDE
  • Locación
  • Microsoft
  • PowerShell
  • Recurso
  • Ubicación
  • Visual Studio

7. Literatura & Enlaces

[1]: Twenty C# Questions Explained – http://www.microsoftvirtualacademy.com
[2]: Console application – Wikipedia, the free encyclopedia – https://en.wikipedia.org/wiki/Console_application
[3]: Windows PowerShell – Wikipedia, the free encyclopedia – https://en.wikipedia.org/wiki/Windows_PowerShell
[4]: Assembly Class (System.Reflection) – http://msdn.microsoft.com/en-us/library/system.reflection.assembly(v=vs.110).aspx
[5]: R1-3 Creación y Uso de un Módulo | OrtizOL – Experiencias Construcción Software (xCSw) – http://ortizol.blogspot.com/2013/10/r1-3-creacion-y-uso-de-un-modulo.html
[6]: Assembly.GetExecutingAssembly Method (System.Reflection) – http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getexecutingassembly(v=vs.110).aspx
[7]: Assembly.Location Property (System.Reflection) – http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location(v=vs.110).aspx
[8]: Path.GetDirectoryName Method (System.IO) – http://msdn.microsoft.com/en-us/library/system.io.path.getdirectoryname(v=vs.110).aspx

J

APO1 – Nivel 3 en C#: Notas Curso

Índice

0. Introducción
1. Enunaciado
1.1 Vista previa de la aplicación (en Java)
2. Requerimientos
2.1 R1: Registrar o cambiar nota
2.2 R2: Calcular el promedio de las notas de los estudiantes
2.3 R3: Calcular el número de estudiantes del promedio
3. Modelo del Mundo del Problema
4. Modelo de la Interfaz Gráfica de Usuario
5. Modelo de Pruebas Unitarias
6. Exploración de c´doigo
6.1 Modelo
6.2 GUI
7. Recursos
7.1 Java 
7.2 C#
7.2.1 Solución y proyectos
7.2.2 Documentación del código fuente de la aplicación
7.2.3 Aplicación en ejecución
7.2.3.1 Capturas de pantalla
7.2.3.2 Vídeo
8. Cambios
9. Conclusiones
10. Glosario
11. Literatura & Enlaces

0. Introducción

Empezamos con la traducción de los ejercicios del Nivel 3 de Algorítmica y Programación Orientada a Objetos (APO1) del proyecto Cupi2 del Departamento de Ingeniería de Sistemas y Computación de la Universidad de los Andes (Colombia). En esta oportunidad crearemos una aplicación para el manejo de notas de un curso que está compuesto hasta por doce (12) estudiantes. En este nivel se hará ahínco en el uso de contenedores de valores de tamaño fijo como elementos de modelaje de atributos cuyo valor es una secuencia de objetos. A lo anterior, también se suma el uso recurrente de instrucciones de repetición para la modificación, recorrido del contenido de la secuencia de objetos.


[Nota: Se respetará cada uno de los elementos que pertenecen al proyecto de Cupi2 del Departamento de Ingeniería de Sistemas y Computación de la Universidad de los Andes. El uso de este material intelectual es única y expresamente para la difusión de conocimiento de tecnologías de desarrollo y las metodologías inherentes a esta profesión.]

1. Enunciado

Descripción del planteamiento del problema para la aplicación Notas Curso originalmente especificado para su desarrollo en el lenguaje de programación Java. (Versión original en [4].)

Se necesita crear una aplicación para manejar las notas para un curso. El curso es visto por doce (12) estudiantes. Para cada estudiante se deber poder registrar la nota definitiva que sacó en el curso.

Adicionalmente, la aplicación debe permitir calcular el promedio del curso, y el número de estudiantes que tienen una nota por encima del promedio.

1.1 Vista previa de la aplicación (en Java)

En la Figura 1 se muestra la interfaz gráfica de usuario de la aplicación desarrollada en lenguaje de programación Java.


Aplicación Notas Curso en Java
Figura 1. Aplicación Notas Curso en Java.

2. Requerimientos

A continuación el conjunto de requerimientos funcionales identificados en la etapa de análisis del planteamiento del problema (sección 1) de Notas Curso. (Fuente: [6].]

2.1 R1: Registrar o cambiar nota

Código: R1

Descripción: Registrar o cambiar nota

Resumen: Se deben poder agregar/modificar la nota de cualquier estudiante del curso.

Entradas:
  • Identificador del estudiante a modificar
  • Nueva nota para el estudiante
Resultados:
  • El estudiante tiene registrar su nueva nota

2.2 R2: Calcular el promedio de las notas de los estudiantes

Código: R2

Descripción: Calcular el promedio de las notas de los estudiantes.

Resumen: Se deben poder calcular el promedio de todas las notas de los estudiantes del curso.

Entradas: «Ninguna»

Resultados:
  • El promedio de las notas del curso. De ser la suma de todas las notas dividido en el número de estudiantes.

2.3 R3: Calcular el número de estudiantes por encima del promedio

Código: R3

Descripción: Calcular el número de estudiantes por encima del promedio

Resumen: Se quiere saber cuántos estudiantes tienen una nota superior a la nota promedio del curso.

Entradas: «Ninguna»

Resultados:
  • Número de estudiantes con una nota superior a la nota promedio del curso.

3. Modelo del Mundo del Problema

En el diagrama UML de la Figura 2 se esquematiza el modelo del mundo del problema para la aplicación Notas Curso. (En la sección 8 se resaltan los cambios realizados sobre el modelo original hallado en [7].)


Modelo del mundo del problema.
Figura 2. Modelo del mundo del problema.

4. Modelo de la Interfaz Gráfica de Usuario

En la Figura 3 se esquematiza el diagrama UML con los artefactos integrales de la interfaz gráfica de usuario. (Este modelo es una adaptación de la versión original [8].)


Modelo de la intefaz gráfica de usuario.
Figura 3. Modelo de la intefaz gráfica de usuario.

5. Modelo de Pruebas Unitarias

El diagrama UML esquematizado en la Figura 4 presenta el modelo de pruebas unitarias. (Original en [9].)


Modelo de pruebas unitarias
Figura 4. Modelo de pruebas unitarias.

6. Exploración de Código C#

6.1 Modelo
Artefactos del modelo del mundo del problema

6.2 GUI
Artefactos integrales del modelo de interfaz gráfica de usuario.

7.1 Java

Conjunto de recursos del proyecto en su versión original [4]:

7.2.1 Solución y proyectos

Enlaces de descarga de archivo ZIP con la solución y los proyectos integrales de la aplicación:

7.2.2 Documentión de código fuente de la aplicación

Enlace de descarga de la documentación (formato CHM) del código fuente C# de la aplicación.

7.2.3 Aplicación en ejecución

7.2.3.1 Capturas de pantalla
Notas Curso en C#
Figura 5. Notas Curso en C#.

7.2.3.2 Vídeo

8. Cambios

Cambios en el diseño de la clase Curso:
  • El método darNota renombrado a ObtenerNota.

9. Conclusiones

A pesar de que en los ejercicios de los niveles 2 y 3 ya habíamos trabajado con estructuras de datos simples (secuenciales) para la manipulación de grupos de atributos, a partir de este ejercicio se hará especial énfasis en su uso. Aquí, por ejemplo manipulamos grupos de controles -Label y Button- como grupos de atributos; esto facilito en gran medida crear varios controles de los tipos correspondientes de forma más ágil que si lo hubiéramos hecho uno por uno. En el próximo ejercucio crearemos (trudiciremos) una aplicación para la administración de las reservas en un vuelo.

10. Glosario

  • Arreglo
  • Atributo
  • Clase
  • Grupo de atributos
  • Modelo
  • UML
  • Uniandes
  • Universidad de los Andes
  • Vector

11. Literatura & Enlaces

[1]: Universidad de los Andes – http://www.uniandes.edu.co
[2]: Departamento de Ingeniería de Sistemas y Computación – http://sistemas.uniandes.edu.co
[3]: Proyecto Cupi2 – http://cupi2.uniandes.edu.co/sitio
[4]: Notas Curso – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso
[5]: Enunciado – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso
[6]: Requerimientos – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/requerimientos-1660
[7]: Modelo Conceptual – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/modeloconceptual-n3notascurso
[8]: Interfaz – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/interfaz-n3notascurso
[9]: Pruebas – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/pruebas-n3notascurso
[10]: JavaDoc – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/javadoc-n3notascurso
[11]: Visualización Código – http://cupi2.uniandes.edu.co/sitio/index.php/cursos/apo1/nivel-3/notas-curso/javadoc-n3notascurso

J

Receta C# No. 4-12: Identificar Cuándo un Thread ha Finalizado su Ejecución

Índice

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Propiedad IsAlive
3.2 Método Join
4. Práctica: Código C#
5. Artefactos
6. Conclusiones
7. Glosario
8. Literatura & Enlaces

0. Introducción

En esta receta C# comprenderemos cómo identificar la completitud de la ejecución de un thread. Con esta habilidad que aprenderemos tanto conceptual como a nivel práctico, reconoceremos los dos métodos disponibles para esta tarea: uso de la propiedad Thread.IsAlive y el uso del método Join (también de la misma clase). Veremos cuál de estos resulta más eficiente o apropiado usando código fuente C#.

1. Problema

A medida que finaliza su ejecución un thread o un conjunto de threads, necesitamos realizar otras operaciones dependientes de su completitud; para esto requerimos conocer los detalles del proceso de identificación de cuándo un thread ha finalizado la ejecución de su proceso subyacente.

2. Solución

La clase Thread cuenta con dos elementos de programa (miembros) que resuelven el problema de identificación de completitud de la ejecución de un thread. Se trata de los siguientes miembros:
  • Propiedad Thread.IsAlive, y 
  • el método sobrecargado Join.

3. Discusión de la Solución

Comencemos por detallar el uso de los elementos de solución identificados en la sección 2.

3.1 Propiedad IsAlive

Con la propiedad IsAlive [2] (System.Threading.Thread) podemos obtener un valor lógico (o booleano) que indica el estado de ejecución de un thread. Esta es su firma:

public bool IsAlive { get; }

Descripción puntual:
  • Valor de propiedad:
    • Tipo System.Boolean: true si el thread se haya en ejecución; en caso contrario, false.
A pesar de su utilidad para conocer la actividad de ejecución de un thread, el uso de esta propiedad resulta ineficiente debido a que se tiene que recurrir a una construcción iteración while o  for, la cual que generará varios ciclos o tiempo de procesador hasta que se complete la ejecución del thread en cuestión. Comprendamos esto a través de un ejemplo:

Notemos con especial cuidado la línea 19: aquí realizamos comprobaciones constantes obteniendo el valor dela propiedad IsAlive del thread (t) que se haya en ejecución. Pueden ocurrir un sinnúmero de comprobaciones y esto recae en uso ineficiente de tiempo de procesador.


Compilación:

  1. csc /target:exe UsoIsAlive.cs


Ejecución assembly:

  1. .\UsoIsAlive.exe


> Prueba de ejecución (ideone.com).


> Prueba de ejecución (local).
Ejecución assembly UsoIsAlive.exe
Figura 1. Ejecución assembly UsoIsAlive.exe.


3.2 Método Join

El método Join cuenta con las siguientes versiones sobrecargadas [3]:
Versiones sobrecargadas del método Thread.Join
Figura 2. Versiones sobrecargadas del método Thread.Join.

La primera versión con firma [4]:

public void Join()

Sencillamente bloquea el thread donde se realiza la llamada y espera a que finaliza su ejecución.

Mientras que con las firmas [5] y [6]:

public bool Join(int millisecondsTimeout)

public bool Join(TimeSpan timeout)

podemos obtener un valor lógico que indica si el thread aún se haya en ejecución. Los argumentos -tipos int y TimeSpan- especifican un tiempo de espera medido en milsegundos y en un intervalo de tiempo; transcurrido este tiempo se comprueba si el thread ya finalizado. Procedamos a elaborar un ejemplo para comprender su verdadera utilidad con la primera versión:

En la línea 17 se invoca el método Join y se espera a el proceso subyacente (Tarea) haya completado su ejecución, mientras tanto Main permanecerá en espera. Evidentemente, esto resulta más eficiente en cuanto al uso de recursos del procesador respecto a la versión de ejemplo con IsAlive de la sección 3.1.

Compilación:

  1. csc /target:exe UsoJoin.cs


Ejecución assembly:

  1. .\UsoJoin.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local).

Ejecución assembly UsoJoin.exe
Figura 3. Ejecución assembly UsoJoin.exe.


4. Práctica: Código C#

Veamos este último ejemplo adaptado de [1] para afianzar el conocimiento de la sección anterior.

En la línea 21 comprobamos si el thread t ha finalizado su ejecución a través de la invocación de Join. Especificamos un tiempo de espera de 2 segundos. Debido a que el tiempo de ejecución (simulado) del método MostrarMensaje es superior a este tiempo, en pantalla se mostrará el mensaje anidado en ifDespués, línea 29, mostramos en pantalla el estado de ejecución del thread consultando la propiedad IsAlive. Bloqueamos nuevamente el thread Main en la línea 32 a través de una invocación a Join. Al finalizar la ejecución MostrarMensaje, insistimos en consultar el estado de ejecución de t en la línea 35.

Compilación:

  1. csc /target:exe FinalizacionEjecucionThread.cs


Ejecución assembly:

  1. .\FinalizacionEjecucionThread.exe


> Prueba de ejecución (ideone.com).

> Prueba de ejecución (local).

Ejecución assembly FinalizacionEjecucionThread.exe
Figura 4. Ejecución assembly FinalizacionEjecucionThread.exe.


5. Artefactos

Enlaces de descarga de los artefactos producidos en el desarrollo de esta receta C#:

6. Conclusiones

Comprendimos a través del uso de la propiedad IsAlive y las versiones sobrecargadas del método Join a identificar cuándo un thread ha finalizado su ejecución. Preferimos el uso (en general) de Join debido a su uso eficiente de recursos, en particular, de ciclos del procesador. En la próxima receta entraremos a practicar sobre el proceso de terminación de la ejecución arbitraria de un thread.

7. Glosario

  • Eficiencia
  • Ejecución
  • Método
  • Propiedad
  • Recurso de máquina
  • Thread
  • Tiempo de procesador

8. Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Thread.IsAlive Property (System.Threading) – http://msdn.microsoft.com/en-us/library/system.threading.thread.isalive%28v=vs.110%29.aspx
[3]: Thread.Join Method (System.Threading) – http://msdn.microsoft.com/en-us/library/System.Threading.Thread.Join%28v=vs.110%29.aspx
[4]: Thread.Join Method (System.Threading) – http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx
[5]: Thread.Join Method (Int32) (System.Threading) – http://msdn.microsoft.com/en-us/library/6b1kkss0(v=vs.110).aspx
[6]: Thread.Join Method (TimeSpan) (System.Threading) – http://msdn.microsoft.com/en-us/library/23f7b1ct(v=vs.110).aspx

M

Receta T-SQL No. 1-13: Ordenar Resultados de una Consulta

Índice

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

0. Introducción

En esta nueva receta T-SQL aprenderemos a ejecutar otras de las operaciones fundamentales a la hora de realizar una consulta: ordenamiento de los resultados. La especificación de un orden en los resultados puede facilitar la interpretación de estos mismos, e inclusive su presentación de aplicaciones consumidoras de datos. Para esto tendemos que usar la cláusula ORDER BY de T-SQL.

1. Problema

Debemos especificar un orden determinado para los resultados generados a partir de una consulta.

2. Solución

Para poder especificar el orden en el que se deben presentar los resultados de una consulta debemos usar la cláusula ORDER BY.

3. Discusión de la Solución

La cláusula ORDER BY [2] ordena los resultados generados por una consulta. Su uso puede aplicarse en situaciones como:
  • Ordenamiento del resultado a partir de un listado de columnas. El orden se establece a partir de la primera columna, luego por la segunda, y así sucesivamente. (La omisión de la cláusula ORDER BY no garantiza un orden específico cada vez que ejecutemos una consulta, es por eso que debe ser explícita su inclusión al final de la consulta.)
  • Generación de un límite de filas en un orden específico.
Sintaxis T-SQL [2]:

ORDER BY order_by_expression
    [ ASC | DESC ] 

Descripción puntual:
  • order_by_expression: Grupo de columnas a partir de las cuales se realizará el orden de los resultados de la consulta. Los nombres de las columnas deben ser únicos y el tamaño total de las columnas no debe exceder los 8060 bytes.
  • ASC | DESC (opcional): Los valores de las columnas son ordenados ya sea en orden ascendente (ASC) o descedente (DESC).
[Nota: Esta descripción sintáctica de la cláusula ORDER BY comprende su uso básico, en [2] podrán encontrar su versión extendida.]


Respecto a la especificación de las columnas en el listado de ordenamiento (order_by_expression) también es posible incluir el número (o índice) que ocupa una columna en la cláusula SELECT, sin embargo, en [2] advierten que recurrir a este método comprende una mala práctica a razón de que se podría cambiar la posición de la columna o agregar unas nuevas en futuros mantenimientos de la consulta, y evidentemente esto afectaría el ordenamiento del resultado.

Veamos un ejemplo básico de uso de la cláusula ORDER BY:

Ordenemos por el ID de producto todos aquellos productos que su nombre empiece por la cadena de caracteres Lock Washer:


SELECT ProductoID AS ‘ID Producto’, Name AS ‘Nombre’
FROM Production.Product
WHERE Name LIKE ‘Lock Washer%’
ORDER BY ProductID;

La ejecución de esta consulta sobre SQL Server 2014 Management Studio genera el siguiente resultado ordenado:


Uso básico de ORDER BY
Figura 1. Uso básico de la clásula ORDER BY.

Notamos que en este resultado los valores de la columna ID Producto mantienen un orden ascendente; esto se debe a que el tipo de orden por defecto es ASC.


[Nota: En la próxima receta trataremos más a fondo el uso de las cláusulas ASC y DESC.]

4. Práctica: Código T-SQL

Recurramos a un ejemplo más complejo para afianzar nuestro conocimiento sobre el uso de la cláusula ORDER BY:

SELECT p.Name AS ‘Nombre Producto’,
h.EndDate AS ‘Fecha’,
h.ListPrice AS ‘Precio de Lista’
FROM Production.Product AS p
INNER JOIN Production.ProductListPriceHistory AS h
ON p.ProductID = h.ProductID
ORDER BY p.Name DESC,
h.EndDate DESC;

En esta consulta queremos mostrar todos aquellos registros de las tablas Product y ProductListPriceHistory que coincidan con el ID de producto (p.ProductID = h.ProductID). (Más adelante comprenderemos el uso de INNER JOIN.) El resultado de esta consulta estará ordenado de forma descedente por el nombre del producto (p.Name), y por la fecha del historial de precios de producto (h.EndDate).

Probemos ejecutando esta consulta para obtener el resultado:
Uso de ORDER BY para ordenamiento historial de productos
Figura 2. Uso de ORDER BY para ordenamiento historial de productos.

5. Conclusiones

Ordenar los resultados de una consulta resulta simple y directo a través del uso de la cláusula ORDER BY. Comprendimos que a través de esta construcción de T-SQL podemos explicitar el orden en que deben mostrarse los resultados a partir de un listado de columnas (nombre o índice, aunque preferible por su nombre debido a futuros mantenimientos de la consulta). También quedó evidenciado que el modo de ordenamiento ya se de valores numéricos o alfabéticos es por defecto ascendente.

6. Glosario

  • Cláusula
  • Columna
  • Consulta
  • Orden

7. Literatura & Enlaces

[1]: SQL Server 2012 T-SQL Recipes – A Problem-Solucion Approach by Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield. Copyright 2012 Jason Brimhall, David Dye, Jonathan Gennick, Andy Roberts, and Wayne Sheffield, 978-1-4302-4200-0.
[2]: ORDER BY Clause (Transact-SQL) – http://msdn.microsoft.com/en-us/library/ms188385.aspx

S
Follow

Get every new post delivered to your Inbox.

Join 80 other followers