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