C# soporta la ejecución paralela de código por medio de multithreading. Un thread o (hilo) es otro camino de ejecución independiente al del proceso principal (o “main” thread) que lo manda a llamar, de esta forma permite la ejecución simultanea con otro hilos.
Un programa de C# empieza con un solo hilo creado automáticamente por el CLR y el sistema operativo. Este hilo se llama “main” thread (o hilo principal) y los otros hilos que son los “worker” threads. El programa se vuelve multhi-threaded (o multi-hilos) al crease threads adicionales.
El CLR asigna a cada thread su propio stack de memoria para mantener las variables locales separadas. Por lo tanto si se hace un método que se ejecuta dos veces en dos threads distintos, entonces existirán dos instancias de las variables en dos stacks. Pero si se desea compartir data entre hilos, se puede hacer si se tiene una referencia a la misma instancia de un objeto. También se puede compartir data entre hilos utilizando campos o variables estáticas (en C# se utiliza la palabra static para definir tales variables).
El problema de compartir data entre hilos es la seguridad entre threads. Sucede los siguiente: puede ser que el thread A empiece a trabajar en las variables o recursos compartidos, cuando thread B todavía esta trabajando en ellos, lo cual afecta el resultado de thread B de una forma no deseada. Similar a lo que pasa cuando dos o mas aplicaciones clientes accesan un registro en una base de datos, definido como concurrencia, solucionado con métodos de control de concurrencia. Para solucionar este problema con los threads se necesita obtener un exclusive lock, de tal forma que el recurso compartido solo puede ser utilizado por un thread a la vez.
Después de esta breve introducción, pueden conocer mas de los threads con ejemplos y todo en esta pagina que considero (ya que me ayudo) muy practica y suficiente para trabajar con threads en C#.
Pero si no desean leer todo el contenido de la página, y desean empezar a trabajar con los threads entonces sigan leyendo. Utilizo los threads en C# para dar una interfase grafica para el usuario que responda durante procesos largos. De esta forma el thread principal Main, podrá estar respondiendo a cualquier requisición del usuario, mientras los hilos trabajan en otras tareas y ventanas.
Para empezar se debe incluir
using System.Threading;
Después de esto creamos el método estático que tendrá todo lo que deseamos correr en el hilo.
private static void metodo()
{
...
}
Si se desea utilizar argumentos, solo se podrá incluir uno. Por lo tanto si se tienen varios argumentos, se pude pasar como argumento un objeto con varias propiedades o un arreglo si todos los argumentos son de un tipo.
private static void metodo(object arg)
{
...
}
Tenemos nuestro metodo ahora a crear el thread. Para crear un thread y ejecutarlo se hace de la siguiente forma:
Thread hilo = new Thread(new ThreadStart(metodo));
Si el metodo tiene un parametro entonces lo declaramos de la siguient eforma
Thread hilo = new Thread(new ParameterizedThreadStart(metodo));
Se declara el hilo con el método que se ejecutara por en el hilo. Para iniciar el thread solo llamamos a:
hilo.Start();
Si deseamos abortar el thread entonces utilizamos Abort(). Para poner a dormir al hilo utilizamos Sleep(n) donde n es el número de segundos que deseamos que el thread duerma.
Dot Net también nos brinda una clase ayudante, BackgroundWorker en el namespace System.ComponentModel para manejar un worker thread. Esta clase nos provee las siguientes bondades:
- Una bandera “cancel” para mandar una señal al thread de que debe terminar sin utilizar el a veces problemático Abort.
- Un protocolo estándar para reportar progreso, completación y cancelación. Muy útil para mostrarle a un usuario como va el trabajo.
- Manejo de excepciones
- La habilidad de actualizar los controles en las Windows Forms del progreso o finalización del thread. También útil para mejorar la interacción con el usuario.
Algo interesante es que el hecho que BackgroundWorker maneja un pool de threads, donde los recicla en vez de estar creando nuevos threads. Entonces si se desea utilizar esta clase hacemos lo siguiente:
Declaramos el objeto
static BackgroundWorker worker = new BackgroundWorker();
Despues le agregamos los eventos a utilzar. Me parece que los eventos mas utiliados son:
DoWork: donde se programa el trabajo qe efectuara el thread
ProgressChange: donde reportamos el progreso de trabajo a un control como ser una progressbar (barra de progreso).
RunWorkerCompleted: que se encarga de ejecutar lo que queramos cuando termina el thread de ejecutarse.
Estos dos últimos eventos son muy importantes para mejorar la interactividad con el usuario y para comunicarse con el main thread, debido que no es permitido que los hilos se comuniquen entre si, ósea “cross thread comunication” no es permitido.
Así adicionamos los métodos que se ejecutaran en los eventos:
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bgWorkerPrintBLs.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
donde los metodos en los parentesis se definen de la siguiente forma:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
//trabajo del thread
}
void bw_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
//intereaccion con el progress bar control
progressBar.Value = e.ProgressPercentage;
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//cuando el thread termina puede ser un messagebox o mensaje
MessageBox.Show(“fin”);
}
Finalmente para inicar el este worker thread haemos los siguiente:
bw.RunWorkerAsync();
Conclusion:
Este es un quickstar para threads en C# lo cual es muy probable que se tenga que utilizar en aplicaciones para Windows que no dejen de responder visualmente cuando les toca ejectuar un proceso de varios segundos. Pero esta es solo la punta de iceberg y el ebook (y fuente teorica para este post) que lei para iniciar con los threads en C# es una buen inicio. Alli tambien encontraran sobre sincronizaciones de threds, seguridad, comunicación entre threads y otra informacion teorica y practica muy util.