Hilos en CSharp

Click here to load reader

  • date post

    22-Jul-2015
  • Category

    Documents

  • view

    712
  • download

    0

Embed Size (px)

Transcript of Hilos en CSharp

UNIVERSIDAD DE CORDOBA FACULTAD DE INGENIERIAS PROGRAMA INGENIERIA DE SISTEMAS CURSO: Electiva de profundizacin I (desarrollo de software libre) TEMA: Hilos de ejecucin en C#. DESCRIPCION: En el presente documento se hace un tratamiento terico-practico sobre el tema de los hilos de ejecucin en el lenguaje de programacin C#, desarrollando para ello una aplicacin de interfaz grafica de usuario (GUI) construida con Widgets (componentes) de GTK# 2.0 y empleando el entorno integrado de desarrollo (IDE) MonoDevelop para GNU/Linux. El programa de ejemplo se construye implementando una clase que encapsula el comportamiento de un hilo de ejecucin que debe realizar una proceso cclico en paralelo para calcular la sumatoria de los trminos de un conjunto de series numricas de modo que puede apreciarse el progreso dentro de un TreeView y con barras de progreso. La implementacin de la clase para la funcionalidad del hilo de ejecucin ilustra adems la declaracin y uso de delegados, como una forma de envi y recepcin de eventos y que permite establecer una relacin dbil entre clases. OBJETIVO: Disear e implementar en el lenguaje C# una clase para representar la funcionalidad de un hilo de ejecucin, de modo que se pueda usar desde aplicaciones de consola y tambin de interfaz grafica de usuario, ejemplificando su uso en un programa de ventana que realice clculos en paralelo de varias sumatorias, mostrando los resultados parciales y finales de esta conjuntamente con una barra de progreso dentro del Widget TreeView. PALABRAS CLAVES: Implementacin de clases en C#, uso de delegados, hilos en C#, la clase Thread de C#, Widgets, propiedades y seales, el Widget TreeView.

Lic. Luis Roberto Olascoaga Surmay

1. HILOS DE EJECUCION Un hilo o hilo de ejecucin es un concepto que permite implementar en un programa la capacidad de ejecutar dos o ms tareas (procesos) de forma paralela, es decir, de manera simultanea, de modo que el programa no tenga que esperar a que termine una o ms de las tareas iniciadas por el usuario para responder a nuevas solicitudes de este. Se debe entender que dichas tareas son consideradas actividades que comprenden la realizacin de ciclos que eventualmente pueden tardar, de tal suerte que no deseamos que el programa se pasme ante el usuario mientras tales procesos son finalizado, permitiendo as que el usuario pueda hacer otras cosas con nuestro programa; incluyendo claro est la posibilidad de cancelar o abortar la tarea que ejecutamos en paralelo. Al respecto cabe recordar que el concepto de paralelo (ejecutar dos o ms tareas o procesos de forma simultnea) es posible siempre que tengamos ms de un procesador en nuestra maquina, pues en caso contrario el procesador se conmuta (comparte) con cada proceso y lo hace tan rpido que parece que los procesos funcionan simultneamente. De otra parte para implementar hilos de ejecucin en C# requerimos el ensamblado System.Threading del cual usaremos la clase Thread (hilo) que posee todo lo necesario en materia de propiedades y mtodos para permitirnos realizar tareas en paralelo dentro de nuestros programas. Los mtodos de inters de la clase Thread para este ejemplo son Strat(), Abort() y Sleep(). No obstante crearemos una clase propia que basada en esta (bajo una relacin de composicin con el fin de proteger ciertos mtodos y propiedades de la clase Thread durante la ejecucin del hilo) nos permita modelar y ejecutar una tarea en paralelo de forma cclica, de modo que la tarea a realizar se reciba como un delegado y que el ciclo contenido dentro del hilo ejecute dicha tarea en intervalos fijos de tiempo y la finalizacin de dicho ciclo sea controlada con otro delegado. Este diseo siguiere que el delegado que implementa la tarea debe contener el cuerpo de un ciclo, no el ciclo en s, pues este suponemos lo pone el hilo de la clase que crearemos. Esto no significa que el delegado que implementa la tarea a realizar no pueda tener ciclos, pues si puede tenerlo, pero se recomienda que sean pocos o cortos, o que en su defecto se sincronice bien con el tiempo de espera del hilo. En un caso extremo que no se desee que la tarea se repita porque ya viene con los ciclos para ello, el delegado de continuacin del hilo debera retornar un false para que la tarea no sea repetida.

Lic. Luis Roberto Olascoaga Surmay

2. PRESENTACION EJEMPLO HILOS DE EJECUCION Para el programa de ejemplo consideraremos una aplicacin de interfaz grafica de usuario construida con el entorno de desarrollo MonoDevelop bajo GTK#2.0, de tal suerte que el programa tenga una clase que encapsule la funcionalidad de un hilo de ejecucin, la cual ser usada para ejecutar en paralelo el clculo de la suma de los n primeros trminos de las tres siguientes series numricas: 1. 2*i 2. i/2 + 1 3. 2*i/3 + 1 El usuario debern indicar para cada serie cuantos trminos debern ser sumados y la aplicacin mediante un mismo hilo calculara la sumatoria final de cada una de estas tres series de manera simultnea, presentando en cada momento el valor parcial de las tres sumatorias en un TreeView, que adems tendr una barra de progreso indicando el avance de la tarea. As mismo se dispondr de un botn que le facilite al usuario la detencin o cancelacin del proceso de clculo en cualquier momento una vez haya sido lanzado. 3. CONSTRUCCCION DE LA SOLUCION Para el desarrollar el programa de ejemplo seguiremos los siguientes pasos: a) Entrar en Monodevelop: Aplicaciones + programacin + Monodevelop

Lic. Luis Roberto Olascoaga Surmay

b) Crear una nueva solucin: Archivo + nuevo + solucin.

c) Luego se despliega la siguiente ventana, en la que seleccionamos de la lista de la izquierda el nodo C# y de la lista del centro escogemos proyecto GTK#2.0 y seguidamente indicamos el nombre al proyecto que el caso es Ejem_Hilos, de manera forma opcional con el botn examinar podemos indicar una carpeta distinta a la presentada para guardar el proyecto. De igual forma si marca la opcin crear subdirectorio de solucin por separado, MonoDevelop creara un subdirectorio adcional para los fuentes de la solucin con el mismo nombre de la solucin. Finalmente pulse el botn adelante.

d) Posteriormente se presenta una ventana en la que puede aadir otros proyectos adicionales y opcionales a la solucin, tales como soporte GTK#, proyecto de traduccin e integracin con Unix. Para este caso ninguna de estas opciones son necesarias y por ello no debe marcarlas. La ventana siguiente se observan estas opciones y se aprecia una descripcin de cada una de ellas.

Lic. Luis Roberto Olascoaga Surmay

La siguiente pantalla ilustra el estado inicial de la solucin, donde en el panel izquierdo vemos el inspector de la solucin, mostrando las referencias (ensamblados externos) y las clases que componen el proyecto, para este caso el archivo principal Main.cs que contiene la clase MainClass presentada en el panel de la derecha. Tambin vemos que la definicin de la clase se encuentra dentro de un nombre de espacio (namespace) llamado Ejem_Hilos, que es mismo nombre de la solucin. En la implementacin del mtodo esttico Main vemos una notable diferencia en cuanto a la implementacin del mismo para una aplicacin de consola, pues se usa el ensamblado Gtk, adems de utilizar el objeto Application llamndose algunos de sus mtodos, adems se crea una instancia de la clase MainWindow que representa la ventana principal, de la cual podemos ver su archivo de cdigo fuente llamado MainWindow.cs como se indica en la figura siguiente.

Lic. Luis Roberto Olascoaga Surmay

En este ejercicio no es necesario usar los nombres de espacio por lo que se sugiere quitarlo eliminndolo junto con su llave de apertura ({) y cierre (}), quedando el cdigo anterior como se indica enseguida en la siguiente imagen:

Seguidamente crearemos la clase para el hilo, para ello proceda haciendo click derecho en el nombre de la solucin y tomar las opciones aadir + nuevo archivo:

Con esto se despliega una ventana en la que del panel de la izquierda seleccionamos la opcin general y del panel central tomamos la opcin clase vaca, dndole por nombre THilo como se aprecia en la siguiente imagen. Una vez hecho esto, procedemos a hacer click en el botn nuevo para crear el archivo cs de la clase y entonces codificarlo como a mas adelante se estar indicando.

Lic. Luis Roberto Olascoaga Surmay

Ahora haga doble click en el panel de la solucin sobre el archivo MainWindow.cs desplegndose la siguiente ventana del cdigo fuente de esta:

En la parte inferior de esta ventana observamos dos botones o vistas de la misma, que nos permiten alternar entre el cdigo fuente de la ventana y el diseo visual o grafico de la misma (botn diseador).

Lic. Luis Roberto Olascoaga Surmay

Cuando haga click en el botn diseador debe ver una imagen previa de la ventana como esta:

4. IMPLEMENTACION DE LA CLASE THILO Haga doble click en el nombre del archivo THilo.cs de la solucin y asegrese de que el cdigo de este archivo sea como sigue:using System; using System.Threading; public delegate void TOnTarea(); public delegate bool TOnContinuar(); public class THilo{ private private private private int FPausa; TOnTarea FTarea; TOnContinuar FContinuar; Thread FHilo;

public THilo(){ FPausa=0; FTarea=null; FContinuar=null; FHilo=null; }

Lic. Luis Roberto Olascoaga Surmay

public int Pausa{ set{ FPausa=value; } get{ return FPausa; } } public TOnTarea Tarea{ set{ FTarea=value; } get{ return FTarea; } } public TOnContinuar Continuar{ set{ FContinuar=value; } get{ return FContinuar; } } protected virtual void Procesar(){ if(FTarea!=null && FContinuar!=null){ while(FContinuar()){ FTarea(); Thread.Sleep(FPausa); } Terminar(); } } public void Iniciar(){ if(FHilo==null){ FHilo=new Thread(Procesar); FHilo.Start(); } } public void Terminar(){ if(FHilo!=null){ FHilo.Abort(); FHilo=null; } } }

Lic. Luis Roberto Olascoaga Surmay

En la implementacin de esta clase observamos que usamos el ensamblado System.Threading que es que contiene la definicin de la clase Thread en base a la cual se desarrolla toda la lgica del funcionamiento del hilo. Como otro aspecto importante que vemos en el cdigo de la clase THilo, es la definicin de dos delegados uno tipo void que es empleado para guardar la implementacin de la tarea o proceso que se va a ejecutar en paralelo. As mismo tenemos un delegado de tipo bool que indicara cuando continuar repitiendo la tarea o en su defecto en qu momento se terminara la misma. Note que este delegado se utiliza para controlar el ciclo que se encuentra en el mtodo procesar que es el que se realiza con el hilo. 5. DISEO DE LA VENTANA Haga doble click en el nombre del archivo MainWindow.cs de la solucin y asegrese de estar en la vista de diseo de esta. Para disear la ventana ser necesario usar la barra de herramientas y el inspector de propiedades. Para ver estos, nos vamos por la opcin Ver del men principal y hacemos click en el tem Visual Design, como se indica en la imagen de izquierda. La imagen de la derecha muestra el panel de componentes (barra de herramientas) y debajo de esta el inspector de propiedades con dos fichas en su parte superior: Propiedades y seales (para programar los eventos). Si aun asi no vemos estos paneles puede seleccionarlos entrando por la opcin Ver + Paneles y marcando Barra de herramientas y despus propiedades respectivamente.

Lic. Luis Roberto Olascoaga Surmay

a) Seleccione la ventana y en el inspector de propiedades despliegue el nodo Window Properties (haciendo click en la flecha a la izquierda de este). Ubique la propiedad Title (Titulo de la ventana) y escriba delante de esta Ejemplo hilos, quedando la ventana como se muestra a continuacin.

b) Ahora en la barra de herramientas asegrese de estar en el grupo de contenedores y desde este arrastre hasta la ventana un widget llamado VBox que nos divide la ventana en tres (por defecto) paneles horizontales, como se aprecia en la siguiente imagen.

Lic. Luis Roberto Olascoaga Surmay

c) Como necesitamos solo dos paneles debemos borrar uno cualquiera de ellos, para lo cual haga click derecho sobre el que desea borra y seleccione la opcin eliminar.

d) Sobre el panel superior arrastre un contenedor Table que por defecto tendr una dimensin de tres filas con tres columnas como muestra la siguiente figura:

e) Seguidamente nos aseguramos de que la dimensin de la tabla sea de dos filas con cuatro columnas, para lo cual en el inspector de propiedades despliegue el nodo Table properties de modo que en la propiedad NRows (numero de filas) ponga 2 y en la propiedad NColumns (numero de columnas) asigne 4.

Lic. Luis Roberto Olascoaga Surmay

f)

Seguidamente en las tres primeras celdas de la primera fila de la tabla vamos a arrastrar tres Widgets Label que se encuentran en el grupo componentes de la barra de herramientas. A estas tres etiquetas pngales por ttulo Max uno, Max dos y Max tres respectivamente, seleccionndolas individualmente y en el inspector de propiedades expanda el nodo Label Properties y escriba el texto adecuado en la propiedad LabelProp, como se aprecia en la imagen para el caso del tercer Label.

g) Ahora hasta las tres primeras celdas de la segunda fila de la tabla arrastre tres Widgets Entry del grupo componentes de la barra de herramientas. A estos tres campos de textos pngales por nombre (propiedad Name en el inspector de propiedades) E1, E2 y E3 seleccionndolos individualmente. Adems en el inspector de propiedades para cada Entry expanda el nodo Common Widget Properties y

Lic. Luis Roberto Olascoaga Surmay

ubicndose en la propiedad WidthRequest (ancho del componente) marque la casilla de verificacin (chquela) que est al frente y pngale el valor de 70 (media en pixeles) como puede ver en la imagen siguiente para el caso del tercer Entry.

h) Arrastre de la barra de herramientas del grupo de componentes un Button hasta la celda superior derecha de la tabla y en el inspector de propiedades asegrese de expandir el nodo Button Properties, asignando a la propiedad Button Type el valor Text and Icon, con el fin de que el botn adems de un titulo pueda exhibir un icono junto a este. En la propiedad Icon haga click en el botn de tres puntos que est a la derecha de esta y seleccione el icono (para el ejemplo gtk-apply). En la propiedad Label escriba el texto del botn, que para este ejemplo es iniciar como se puede observar en la siguiente imagen.

Lic. Luis Roberto Olascoaga Surmay

i)

Arrastre otro botn hasta la celda sobrante de la tabla y en el inspector de propiedades en el nodo Button Properties asigne las propiedades indicadas.

Lic. Luis Roberto Olascoaga Surmay

j)

Finalmente en el panel inferior arrastre un componente TreeView al cual en la propiedad Name asgnele Trv.

6. IMPLEMENTACION DE LA VENTANA Pase ahora a la vista de cdigo fuente de la ventana, dentro de la cual declaramos como atributos privados a Max1, Max2 y Max3 de tipo int (entero) para guardar en ellos los valores ingresados en los Entry E1, E2 y E3 respectivamente, que representan el numero de trminos que se sumaran para cada una de las tres series. El atributo entero Pos lo usamos para determinar el trmino i-esimo de cada serie adems de participar en el clculo de porcentaje de progreso de cada serie. Este mismo atributo sirve para establecer cuando el hilo debe terminar lo cual se aprecia en el mtodo Seguir() que responde a la firma del delegado TOnContinuar requerido por la clase THilo. De especial atencin es el cdigo del mtodo CrearColumnas(), en donde se inicializa el modelo del TreeView con tres columnas de tipo string (cadena), int (entero) y double (real), adems observe la forma en la que se crean las columnas, como se asocian esta a las columnas del modelo y los objetos de renderizado (dibujado) que se usan para cada columna con sus respectivas propiedades.

Lic. Luis Roberto Olascoaga Surmay

using System; using Gtk; public partial class MainWindow : Gtk.Window { private private private private int Pos; THilo Hilo; ListStore Mod; int Max1,Max2,Max3;

public MainWindow () : base(Gtk.WindowType.Toplevel){ Build(); Hilo=null; CrearColumnas(); } private void CrearColumnas(){ Mod=new ListStore(typeof(string),typeof(double),typeof(int)); Trv.AppendColumn(new TreeViewColumn("Operacion",new CellRendererText(),"text",0)); Trv.AppendColumn(new TreeViewColumn("Resultado",new CellRendererText(),"text",1)); Trv.AppendColumn(new TreeViewColumn("Progreso",new CellRendererProgress(),"value",2)); Trv.Model=Mod; } private void IniciarModelo(){ Mod.Clear(); Mod.AppendValues("Suma 2*i",0.0,0); Mod.AppendValues("Suma i/2 + 1",0.0,0); Mod.AppendValues("Suma 2*i/3 + 1",0.0,0); } private void IniciarHilo(){ Pos=1; Hilo=new THilo(); Hilo.Pausa=300; Hilo.Tarea=CalcSeries; Hilo.Continuar=Seguir; Hilo.Iniciar(); } private void Sumar(int NumFila,int Porcen,double Aumento){ double Actual; TreeIter Fila; Mod.IterNthChild(out Fila,NumFila); Actual=(double)Mod.GetValue(Fila,1); Mod.SetValue(Fila,1,Actual+Aumento); Mod.SetValue(Fila,2,Porcen); }

Lic. Luis Roberto Olascoaga Surmay

private bool Seguir(){ return (Pos