Cualquier cosa que valga la pena se hace en equipo

Cualquier cosa que valga la pena se hace en equipo

lunes, 26 de abril de 2010

Algo sobre threads en C# - Parte III



OBJETIVO


Veamos ahora un modelo de implementación de threads así como la implementación de dos patrones de desarrollo clásicos: Command y Strategy.-

Para eso vamos a plantearnos un motor batch , es decir un proceso que sea capaz de disparar procesos batch (no interqactivos ) en diferentes hilos de ejecucion


Un motor de procesos batch

Un motor de procesos batch es un software que es capaz de ejecutar procesos batch de modo concurrente y controlado.-

Lo que expondremos acá es un modelo simple del mismo del que después pueda derivarse un motor de uso profesional.-

Lo importante del motor desde el punto de vista de la arquitectura es que se trata de una aplicación que es capaz de manejar múltiples hilos de ejecución, colocar en cada uno de esos hilos un proceso a ejecutar , lanzar dichos procesos , esperar su conclusión y reportar que el proceso finalizo.-


Lo importante del motor desde el punto de vista de la técnica de diseño es que cumple con un patrón de los enunciados por Gamma y otros (GoF [1]).
Este patrón se denomina Command (Comando).

La idea de Comando como patrón de nuestro diseño es un objeto que tenga 3 estadios clásicos: Inicio, ejecución y cierre. Llamaremos a esos métodos como Inicio(), run() y CleanUp().-

Caulquier implementacion que tengo estas 3 tareas sera apra nostros un comando.
Pensemos entonces en una estrcutura de programacion que nos permita sugerir esta plantilla antes de implementarla. Llamamos a esto Interfaz

La Interfaz Comando




using System;


using System.Collections.Generic;
using System.Text;
namespace ThreadPoolTest_5


{


public interface Comando


{


void inicio();


void run();


void cleanup();


}


}

Las clases que implementan la interfaz Comando


Veamos ahora clases concretas de la Interfaz comando. Estas clases implementan los métodos declarados en la interfaz.-

Daremos dos implementaciones una llamada miClase y la otra llamada miOtraClase.-

No tienen nada de particular salvo que ambas implementan a la interfaz Comando.-

La clase miClase

Esta primera funcion solo consumo tiempo de procesamiento

using System;


using System.Collections.Generic;


using System.Text;


using System.Threading;






namespace ThreadPoolTest_5


{


public class miClase : Comando


{


private string _nombre;


private bool _terminado;


public string nombre
{
get { return _nombre; }
set { _nombre = value; }
}





public bool terminado
{
get { return _terminado; }
set { _terminado = value; }
}






public void inicio()


{


_terminado = false;


}





public void run()
{


Console.WriteLine(this.GetType().ToString() + " :Procesando requerimiento '{0}'." +


" Hash: {1}", (string)nombre, Thread.CurrentThread.GetHashCode());


// Simulacion de tiempo de procesamiento


int ticks = Environment.TickCount;


//Trabajar mientras no hayan transcurrido los 2 segundos


while (Environment.TickCount - ticks < 2000) ;




Console.WriteLine("Requerimiento '{0}' procesado",(string)nombre);


}


public void cleanup()
{


_terminado = true;


}


}// de clase


}// de namespace




La clase mi otraclase

Esta segunda funcion calcula el coseno de un angulo en radianes por el metodo de expansion:

using System;


using System.Collections.Generic;


using System.Text;


using System.Threading;





namespace ThreadPoolTest_5
{
public class miOtraClase : Comando
{


private string _nombre;
private bool _terminado;


public string nombre
{
get { return _nombre; }
set { _nombre = value; }
}




public bool terminado
{
get { return _terminado; }
set { _terminado = value; }
}


public void inicio()
{
_terminado = false;
}


public void run()
{


Console.WriteLine(this.GetType().ToString() +": Procesando requerimiento '{0}'." +" Hash: {1}",
(string)nombre, Thread.CurrentThread.GetHashCode());




//Calcular
CalcularCosenos();


Console.WriteLine("Requerimiento '{0}' procesado", (string)nombre);


}


private void CalcularCosenos()
{
const double EPSILON = 0.000000001;
const double DESDE = 0.5346222;
const double HASTA = 3.1415926;
const double PASO = 0.000001999;


double lRadianes = DESDE;


double RadCuadrado;


double s;


double t;


double dFabsLRas;


double dFabsProd;


int k;


string Aux = "";




while (lRadianes < HASTA)
{
k = 0;
t = 1;
s = 1;
RadCuadrado = lRadianes * lRadianes;
dFabsLRas = Math.Abs(t);
dFabsProd = (EPSILON * Math.Abs(s));

//Calculamos la expansion hasta que el termino enesimo no sea mayor a un EPSILON DADO
while (dFabsLRas > dFabsProd)
{


k = k + 2;


t = (-1) * t * s * RadCuadrado/ (k * (k - 1));


// 1 - x^2/2! + x^4/4! - x^8/8! ...


s = s + t;


dFabsProd = (EPSILON * Math.Abs(s));


dFabsLRas = Math.Abs(t);


}


Aux = lRadianes.ToString() + ":" + s.ToString();
Console.WriteLine(Aux);
lRadianes += PASO;


}

}


public void cleanup()
{


_terminado = true;


}


}


}


El motor batch de tareas asíncronas


Ahora vamos a implementar el motor.-

El motor de tareas asíncronas representa una implementación de un modelo de procesamiento de un software capaz de disparar OTRAS tareas según una configuración de tiempos.-

En el ejemplo estamos utilizando 4 conceptos sobre los que el motor esta diseñado:

1- Para que el motor deba ejecutar un proceso en hilos (threads) múltiples usaremos un namespace llamado System.Threading en el que encontramos una clase llamada WaitCallback que es la implementación de un prototipo de función CallBack [2].-

2- La clase WaitCallback, necesita en su constructor un método que cumpla con este prototipo :

private static void MiFuncion(object miObjeto);

De modo que nosotros creamos un método dentro de nuestra clase de implementación del motor que pueda cumplir con este prototipo.-

3- La clase ThreadPool, con métodos estáticos públicos es la que nos permitirá levantar cada uno de los procesos que el motor batch dispara. ThreadPool nos permite definir la cantidad de threads que se abrirán en la sesión así como encolar los procesos que disparamos de modo de ejecutarlos secuencialmente.-

4- EL motor tiene un método llamado Trabaja() que es el que ejecuta la parte central de la tarea. Ahora bien : ¿ Que necesita Trabaja() para hacer su tarea ?.


Necesita una lista de objetos. De alli que Trabaja() tiene como parámetro un contenedor genérico de objetos a procesar.-

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;


namespace ThreadPoolTest_5
{
class MotorBatch
{

public static void trabaja(List <> cm)
{
//EL motor tiene su funcion de disparo y arma un
// handler a ella
WaitCallback callBack;
callBack = new WaitCallback(FuncionAEjecutar);

//El motor ajusta la cantidad de threads por procesador
int minWorker, minIOC;
ThreadPool.GetMinThreads(out minWorker, out minIOC);
// 4 threads asincronicos como minimo, por procesador
ThreadPool.SetMinThreads(4, minIOC);

//El motor lanza las tareas
foreach(object com in cm)
{
ThreadPool.QueueUserWorkItem(callBack, com);
}

//El motor sincroniza el pool de threads
//Hasta terminar
Console.ReadLine();
}

private static void FuncionAEjecutar(object mc)
{
Comando xco = (Comando)mc;
xco.inicio();
xco.run();
xco.cleanup();
}

}//clase

}//namespace




Un programa que utiliza el motor batch

Corresponde ahora que hagamos un programa que utiliza el motor batch para dispara procesos. Lo que haremos será que el motor dispare las implementaciones de Comando que hicimos con miClase y miOtraClase.-
Observemos que es lo que nuestro programa hace ahora:
Siendo que el motor necesita que a su método publico y estático Trabaja() se le pase como parámetro un genérico de tipo con objetos, lo primero que hace la aplicación que llamara al motor es generar una lista de dicho tipo.
Podemos ir viendo ya que esta generación es una tarea que debería estar controlada en el sentido que no podemos colocar cualquier tipo de objetos en esa lista sino solamente aquellos que deriven de Comando. Ese punto lo revisaremos en las mejoras que haremos al motor batch.-



using System;
using System.Threading;
using System.Collections.Generic;

namespace ThreadPoolTest_5
{

//
class MainApp
{
static void Main()
{
//Alguien carga los comandos en una lista y el motor
//la invoca
List <> cm = ListaDeComandos();

MotorBatch.trabaja(cm);

}

static public List ListaDeComandos()
{
miClase mc;
miOtraClase moc;
List<> comandos = new List<> ();

for (int i = 0; i <>

{

if ((i % 2) != 0)

{

mc = new miClase();

mc.nombre = "Hilo nro: " + i; comandos.Add(mc);

}

else

{

moc = new miOtraClase();

moc.nombre = "Hilo nro: " + i;

comandos.Add(moc);

}

}//for..


return comandos;

}//

}

}





Vemos en esta sencillo ejemplo las ideas basixas del motor batch y su rapida implementaciom
Dejamos para una IV parte de esta serie una version con mejoras en el diseño del motor
**************************************
[1] GoF es el acrónimo de “Gang of Four” o sea “La banda de los cuatro”. El nombre es una broma que liga la denominación de los 4 autores del libro Pattern Designs (Gamma, Helms, Jhonson y Vlissides)con la denominación de un grupo político del poder chino posterior a la muerte de Mao Tse Tung al que se lo acuso de traición por sus posturas radicalizadas durante la revolución cultural .-

1 comentario:

Unknown dijo...

Estimados,
My buen articulo, acabo de implementar su solucion de motor batch, pero una pregunta, si la funcion a ejecutar (dentro del metodo Run()) da un error, veo que todo el programa se cuelga, pese a que el mismo lo capturo en bloques try catch.
Un ejemplo claro de esto fue que el metodo a ejecutar (Calclular cosenos en su ejemplo) realizo una llamada a un web service, cuando dicho web service esta activo el motor batch funciona de maravilla, pero al realizar una prueba (desconectando el web service) en el metodo donde llamo al web service cae en el bloque catch y de ahi todo el programa se cuelga (se detiene la ejecucion del thread pool y da la sensacion como que dejara de trabajar)
Podrian indicarme como se manejaria estos tipos de problemas en su motor???

Gracias de antemano, un abrazo desde su pais hermano Ecuador.

Att. Juan Carlos Orellana