jueves, 9 de abril de 2015

Receta Multithreading en C# No. 2-3: Limitar el Acceso a un Recurso Compartido con SemaphoreSlim

Índice

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

0. Introducción

En esta nueva receta multithreading en C# aprenderemos cómo limitar el acceso a un recurso compartido con la clase SemaphoreSlim. Ya veremos que esto nos puede resultar útil para ciertas restricciones que puedieran ser impuestas sobre un recurso compartido: archivo, base de datos, recurso de red, etc. El ejemplo nos ilustrará cómo podemos utilizar ese artefacto para simular el acceso concurrente a una base de datos. (Evidentemente este tipo de ejemplos será únicamente para ilustrar el concepto base de esta clase y su funcionalidad.)

1. Problema

Requerimos de una técnica programática en C# para limitar el número de threads que pueden acceder concurrentemente a un recurso de acceso compartido.

2. Solución

La biliblioteca base de clases (BCL) de .NET cuenta con la clase `SemaphoreSlim` para responder a el requerimiento previo.

3. Discusión de la Solución

La clase `SemaphoreSlim` reside en el espacio de nombres System.Threading. Esta clase básicamente constituye [1] una versión alternativa (lightweight) a la clase `Semaphore` [4, 3] que para el propósito solicitado permite limitar el número de threads (o directus et simplux, hilos) que tienen permitido acceder a un recurso compartido de forma concurrente [2].

3.1 Propiedades sobresalientes de `SemaphoreSlim`


La clase `Semaphore` cuenta con las propiedades `AvailableWaitHandle`, y `CurrentCount`:
Propiedades de SemaphoreSlim
Figura 1. Propiedades de SemaphoreSlim [2].
Como se describe en la Figura 1, la propiedad `AvailableWaitHandle` retorna el objeto `WaitHandle` que es requerido para realizar esperas sobre el semáforo. Mientras que la propiedad `CurrentCount` retorna el número de threads permitidos sobre el recurso compartido. Esta última propiedad se establece al instanciar una clase de `Semaphore`, dado que se requiere especificar la cantidad límite de threads que podrán acceder de forma concurrente al recurso compartido en cuestión.

3.2 Constructores de `SemaphoreSlim`


Descripción general de los constructores disponibles en `SemaphoreSlim` [2]:
Constructores de SemaphoreSlim
Figura 2. Constructores de `SemaphoreSlim` [2].

3.2 Notas importantes de uso de `SemaphoreSlim`

Desde [1] nos advierten:
«There is no sense in using it -`SemaphoreSlim`-, except in one very important scenario, we can create a named semaphore like a named mutes and use it to synchronize threads in different programs. Semaphore does not use Windows Kernel semaphores and does not support interprocess synchronization, so use Semaphore in this case.

4. Práctica: Código C#

Pasemos a crear un ejemplo sencillo pero funcionalmente ilustrativo de uso de la clase `SemaphoreSlim` en el que vamos a simular el acceso a una base de datos (recurso compartido) por múltiples threads:

En las líneas 35-49 se declara el método `AccederBaseDatos`; este método lleva a cabo las siguientes operaciones:

  1. Linea 37: Muestra mensaje en la salida estándar (consola) anunciando el intento de acceso a la base de datos por parte del thread actual.
  2. Línea 38: Bloquea el thread actual hasta que tenga permiso de acceder a la base de datos.
  3. Línea 40: Anuncia el acceso recién permitido a la base de datos al thread.
  4. Línea 43: Simula tiempo de una operación transaccional o de consulta a la base de datos.
  5. Línea 45: Anuncia que el thread ha finalizado su ejecución.
  6. Línea 48: Libera el thread desde pool de threads.
Por otro parte, el método de inicio de ejecución de la aplicación, `Main` (líneas 12-31), se encarga de instanciar hasta 6 threads y ponerlos en ejecución sobre el método `AccederBaseDatos` (línea 24). Cada thread recibe un nombre y un tiempo de espera (tiempo simulado de ejecución de la operación transaccional o de consulta): líneas 20 y 22, respectivamente.

Compilación:


  1. csc /t:exe AccesoBaseDatosConSemaphoreSlim.cs


Ejecución assembly:


  1. .\AccesoBaseDatosConSemaphoreSlim.exe



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

5. Conclusiones

En esta receta multithreading hemos aprendido a entender y a usar la clase `SemaphoreSlim` del namespace System.Threading para controlar el límite de threads que pueden acceder de forma concurrente a un recurso compartido. En el ejemplo de la sección 4 se simuló el acceso concurrente a una base de datos por parte de 6 threads: sólo pueden acceder de manera simultánea 4 threads, los 2 restantes deberán esperar a que uno o más threads finalicen las operaciones transaccionales o de consulta especificadas y la liberación correspondiente. En la próxima receta multithreading aprederemos a usar la clase `AutoResetEvent` para el envío de notificaciones entre threads.

6. Glosario

  • .NET
  • BCL
  • Hilo
  • Multithreading
  • Recurso compartido
  • Thread

7. Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: SemaphoreSlim Class (System.Threading) - https://msdn.microsoft.com/en-us/library/vstudio/system.threading.semaphoreslim(v=vs.100).aspx
[3]: Semaphore Class (System.Threading) - https://msdn.microsoft.com/en-us/library/vstudio/system.threading.semaphore(v=vs.100).aspx
[4]: OrtizOL - Experiencias Construcción Software (xCSw): Receta C# No. 4-10: Sincronización de Múltiples Threads usando un Semáforo - http://ortizol.blogspot.com/2014/07/receta-csharp-no-4-10-sincronizacion-de-multiples-threads-usando-un-semaforo.html

J

lunes, 6 de abril de 2015

Tipos Anónimos en C#

Índice

0. Introducción
1. Tipo Anónimo
2. Traducción de un Tipo Anónimo
3. LINQ y Tipos Anónimos
4. Características Extra de Tipos Anónimos
4.1 Determinación de identificadores
4.2 Tipos anónimos idénticos
4.3 Sobreescritura del método Equals
5. Reglas y Comentarios de Tipos Anónimos
6. Conclusiones
7. Glosario
8. Literatura & Enlaces

0. Introducción

En este artículo estudiáremos con detalle el uso y propósito de tipos anónimos. Este tipo de construcción de C#, como veremos, nos facilitará la definición de miembros de tipo (exactamente, propiedades de sólo lectura) sin recurrir a la definición explícita de una clase o estructura en tiempo de diseño. Esta tarea la realizará automáticamente el compilador de C#. A través de ejemplos de uso entenderemos que su utilidad principal está orientada a la construcción o diseño de consultas LINQ.

1. Tipo Anónimo

Un tipo anónimo es el resultado de definición de una clase sin nombre con únicamente propiedades de sólo lectura por parte del compilador. Este trabajo que realiza el compilador de forma automática requiere de definiciones básicas y sencillas de valores de otros tipos de datos por parte del programador; para lograrlo, se debe recurrir a estas construcciones del lenguaje:
  • La palabra clave new
  • Inicializador de objetos [2]
var tipoDesconocido = new { Nombre = "Fedor", Edad = 30 }; 

En primer lugar se define el tipo de la variable tipoDesconocido con var; luego utilizamos el operador new seguido de un objeto de inicialización, que en este caso tiene definidos dos propiedades con nombres formales. El compilador de C# se encargará de inferir los tipos de los datos basado en los valores asignados a las propiedades de sólo lectura:
  • Nombre: String
  • Edad: int

Vale agregar que en Visual Studio podemos reconocer los tipos asociados de las propiedades del tipo anónimo en tiempo de diseño. Para el ejemplo de uso anterior:
Inferencia de tipos en Visual Studio
Figura 1. Inferencia de tipos de propiedades de tipo anónimo en Visual Studio.

2. Traducción de un Tipo Anónimo

Ya hemos mencionado que el compilador de C# es capaz de inferir los tipos de las propiedades basado en el tipo de los valores asignados. Sin embargo, es importante tener conocimiento de lo que sucede detrás de cámaras: reconocer la traducción aproximada que lleva a cabo el compilador a partir de un tipo anónimo. Veamos:

Notemos como en la línea 1 se declara la clase como el modificador internal junto con el nombre generado por el compilador. (NombreTipoAnonimoGenerado es sólo un nombre ilustrativo.) Sobre las líneas 3-4 se definen los campos de miembro con los tipos inferidos (i.e., stringint). También se define el constructor (líneas 6-10) con los parámetros formales y los tipos inferidos. Se defininen, además las propiedades que corresponden con los nombres (capitalizados) y tipos inferidos. En [1] añaden que los métodos miembro GetHashCode, Equals, y ToString son sobreescritos con versiones e implementaciones propias del tipo anónimo.


Para extendernos un poco más: cuando creamos un tipo anónimo, la traducción hecha por el compilador quedaría expresada de la siguiente manera:


var tipoDesconocido = new NombreTipoAnonimoGenerado ("Fedor", 30 );

3. LINQ y Tipos Anónimos

El lenguaje integrado de consulta (LINQ [5]) además de poseer capacidades abstractas para consultar datos sobre distintas fuentes de datos (e.g., objetos, bases de datos, XML), recurre al uso de tipos anónimos para la construcción implícita de objetos con las propiedades de los resultados de las consultas efectuadas a una fuente de datos.


Para ilustrar con código fuente C# la definición anterior, escribamos este ejemplo que consta de un arreglo de tipos anónimos y que para facilitar su manejo lo llamaremos personas; así:


var personas = new[]
{
new { Id = 1001, Nombre = "Vallejo" },
new { Id = 1002, Nombre = "Asimov" },
new { Id = 1003, Nombre = "Fedor" }
}

foreach (var persona in personas)
{
Console.WriteLine("ID: {0}", persona.Id);
Console.WriteLine("Nombre: {0}", persona.Nombre);
}


Además de iterar la lista de elementos de tipos anónimos podemos crear una consulta LINQ para enlistar cada uno de los datos asociados a la persona (i.e., Id, Nombre):


var resultado = from persona in personas
select new { persona.Id, persona.Nombre };


En esta sentencia de LINQ, exactamente en new { persona.Id, persona.Nombre } se crea de manera implícita un tipo anónimo con las propiedades Id, y Nombre por cada elemento en personas. A partir de aquí el compilador de C# realiza el mismo proceso iterativo de traducción a un tipo anónimo similar al que se ilustró en la sección anterior.

4. Características Extra de Tipos Anónimos

4.1 Determinación de identificadores

Podemos especificar variables ya definidas y hacerlas parte del objeto de inicialización para la creación de un tipo anónimo. Esto lo logramos simplemente incluyendo en la lista de inicialización uno o más variables de tipos conocidos previamente definidas:

int Edad = 30;
var tipoAnonimo = new { Nombre = "Fedor", Edad, Edad.ToString().Length };

El segundo elemento de la lista de inicialización, Edad, se convertirá en una propiedad del objeto anónimo. Esta misma operación también ocurrirá para la propiedad Length de String.

4.2 Tipos anónimos identicos

Dos tipos anónimos son idénticos o iguales si cumplen con las siguientes características:
  • Las propiedades enlistadas tienen exactamente los mismos tipos de datos subyacentes.
  • Los nombres de las propiedades son idénticos.
Ilustremos esta característica con el siguiente ejemplo:

var tipoAnonimo1 = new { X = 2, Y = 4 };
var tipoAnonimo2 = new { X = 2, Y = 4 };

Console.WriteLine( tipoAnonimo1.GetType() == tipoAnonimo2.GetType() ); // True

4.3 Sobreescritura del método Equals

El método Equals [6] es sobreescrito por el compilador y funciona de la siguiente manera:

Console.WriteLine( tipoAnonimo1 == tipoAnonimo2 ); // False
Console.WriteLine( tipoAnonimo1.Equals(tipoAnonimo2) ); // True

5. Reglas y Comentarios de Tipos Anónimos

Estas son algunas reglas y comentarios extra de tipos anónimos [3]:
  • Todo tipo anónimo deriva directamente de object [6].
  • El compilador crea un nombre para cada tipo anónimo que es innaccesible en tiempo de diseño.
  • La CLR considera todo tipo anónimo de forma análoga a los tipos concretos o definidos por el programador.
  • No es posible declarar un campo, una propiedad, un evento [7], o tipo de retorno como tipo anónimo.
  • Los parámetros formales de un método, propiedad, constructor, o índice no admiten tipos anónimos.
  • Hay que considerar que el uso excesivo de tipos anónimos va en contra de la propiedad innata de C#: tipeado fuerte.

6. Conclusiones

Hemos explorado los fundamentos de tipos anónimos. Este tipo de construcciones, como hemos visto, nos facilita la composición de tipos de datos existencia o acción efímera. Su poderosa utilidad reside en la composición de consultas bajo el lenguaje de consulta integrado LINQ. Uno de los aspectos a reconocer está la traducción (aproximada) que hace el compilador para su tratamiento interno bajo la CLR. Al final exploramos otras características relevantes, además de reglas y comentarios de uso de tipos anónimos (e.g., la derivación directa de object.). En el próximo artículo C# descubriremos la vinculación dinámica (dynamic binding) en C#.

7. Glosario

  • CLR
  • LINQ
  • Tipeado fuerte
  • Tipo
  • Tipo anónimo

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]: Inicializadores de Objetos en C# - http://ortizol.blogspot.com/2013/10/inicializadores-de-objetos-en-c.html
[3]: Anonymous Types (C# Programming Guide) - https://msdn.microsoft.com/en-us/library/bb397696.aspx?f=255&MSPPError=-2147217396
[4]: Anonymous types - http://www.codeproject.com/Tips/133941/Anonymous-types
[5]: Language Integrated Query - Wikipedia, the free encyclopedia - http://en.wikipedia.org/wiki/Language_Integrated_Query
[6]: La Clase Object y sus Miembros en C# - http://ortizol.blogspot.com/2014/04/la-clase-object-y-sus-miembros-en-c.html
[7]: Eventos en C# - Parte 1: Introducción a Eventos - http://ortizol.blogspot.com/2014/06/eventos-en-csharp-parte-1-introduccion-a-eventos.html


J

domingo, 5 de abril de 2015

Receta T-SQL No. 2.3: Sentencia de Selección IF...THEN...ELSE

Í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

Continuamos con la elaboración de recetas T-SQL. En esta oportunidad aprenderemos a usar el bloque de selección IF...THEN...ELSE para controlar el flujo de ejecución de un grupo de sentencias T-SQL. Conoceremos la sintaxis declarativa como también algunos ejemplos usando la base de datos AdventureWorks para afianzar e intuir el funcionamiento de este elemento de programa.

1. Problema

Queremos controlar el flujo de ejecución de una sentencia o más sentencias T-SQL.

2. Solución

T-SQL cuenta con el elemento de programa o sentencia de selección múltiple IF...THEN...ELSE para dar solución a este problema de control del flujo de ejecución de sentencias T-SQL.

3. Discusión de la Solución

La sentencia IF...THEN...ELSE [2] comprende la siguiente sintaxis:

IF Expresión_Booleana
{ sentencia_SQL | bloque_sentencias }
[ ELSE
{ sentencia_SQL | bloque_sentencias }
]

Donde la sección Expressión_Booleana es cualquier predicado lógico que resulte en uno de los valores lógicos FALSO (FALSE) o VERDADERO (TRUE); si esta sección da como resultado el valor lógico VERDADERO se ejecutará uno (sentencia_SQL) o más (bloque_sentencias) sentencias del bloque IF. En caso contrario (opcional), se ejecutará el bloque ELSE si es que estuviera presente.

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

A través del siguiente ejemplo básico de uso de esta sentencia IF...THEN...ELSE declararemos una variable y evaluaremos su valor para tomar uno entre dos caminos de ejecución posibles:


En la línea 1 creamos la variable entera (int) QuerySelector. El valor de esta variable, 3, será evaluado (comparado) en la línea 2 en la sentencia IF con la literal constante 1. El resultado de la evaluación previa será FALSO (FALSE) y se ejecutarán el conjunto de sentencias del bloque ELSE de las líneas 10-15. Cuando ejecutamos esta sentencia en Microsoft SQL Server Managament Studio, obtenemos el siguiente resultado:
Sentencia de Selección IF...THEN...ELSE - Ejemplo 1
Figura 1. Resultado ejecución ejemplo 1.

Vale anotar que el bloque IF permite la evaluación de múltiples predicados o sentencias booleanas; por ejemplo:

IF (@QuerySelector = 1 OR @QuerySelector = 3) AND (NOT @QuerySelector IS NULL)


Al reemplazar este bloque IF con el del ejemplo anterior, obtendremos el siguiente resultado:
Figura 1. Resultado ejecución ejemplo 2
Figura 2. Figura 1. Resultado ejecución ejemplo 2.

5. Conclusiones

Hemos explorado y puesto en práctica los fundamentales de uso de la sentencia de selección IF...THEN...ELSE. Esta sentencia nos permite controlar el flujo de control de uno o más bloques de construcción. Esto nos resulta de utilidad para obtener caminos de ejecución diferentes dependiendo del valor de una determinada variable en un momento de ejecución dado.

6. Glosario

  • Bloque
  • Expresión booleana
  • Sentencia

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]: Using IF...ELSE - https://technet.microsoft.com/en-us/library/ms187471(v=sql.105).aspx


S