miércoles, 8 de septiembre de 2010

Algunos dolores de cabeza con threads

Esta entrada va relacionada con mi programa RSS Reader que comente en el post pasado, estoy escribiendo esto por partes para que tenga sentido y sea un poco mas granular, espero que se entienda.

Retomando el post pasado mi programa necesitaba una manera para agregar nuevos folders y ponerlos en el TreeView.

La solución al problema es trivial pero funcional. De nuevo con en la ocasión anterior primero diseñe la pantalla para crear un Folder Nuevo, nada complicado.



Esta pantalla contiene los elementos necesarios para el trabajo un label que muestra al usuario los posibles errores, por ejemplo en caso de que no se diera un nombre al folder, un label que indica para que es el control, y un para de botones para aceptar y cancel, bastante obvio.

El código de la forma anterior era muy sencillo, hasta este momento no estoy creado ninguna estructura de folders en el sistema operativo solo los agrego al tree view, el plan es pasar todo a un data set y grabarlo todo a una pequeña DB.

La siguiente Etapa fue la creación de la pantalla para agregar un nuevo feed, recorde algunos readers que te permitian chekar que podían obtener el feed correctamente antes de que lo agregaras a los demas, cree una pantalla también bastante simple pero que me dio algunos dolores de cabeza y dos o tres desveladas intentando arreglar. Así es como luce ahorita.

También es bastante simple no son muchos controles, pero el que me me dio problemas fue el progress bar, el problema era que cuando hacía el WebRequest para traer el feed de la dirección dada en el cuadro de texto toda la UI se quedaba paralizada y eso incluía el progress bar, de hecho en ocaciones parecía quedarse en el limbo no daba error en el feed ni tampoco lo traía y presionar cancelar no era una opción porque todo el UI estaba trabado.

New to threads


Bueno si la neta soy nuevo con los threads, si los he usado pero para cosas pequeñas como tareas en la escuela nada complicado, el problema es que usarlos en .net es un poco mas difícil, leyendo en la documentación de C# empeze a leer sobre los threads System.Threading estuve tentado por el thread pool pero preferi hacer mis threads a mano despues de un rato de lectura así que dispare mi primer thread y llegaron los problemas, primero cree mi thread

Thread myThread = new Thread(new ThreadStart());

Luego me di cuenta que necesitaba el nombre del metodo que corria el trabaja que quería que hiciera el thread, yo llamaba a un metodo llamado

public void feedManage()

Ahora para llamar al thread de forma corre e iniciarlo quedo algo como esto:

Thread myThread = new Thread(new ThreadStart(feedManage));
   myThread.Start() //Empezar a correr el thread

Este se encargaba del hacer el WebRequest y el HttpWebResponse para obtener el Xml del feed
el problema es que yo accesaba los labels de la UI para mostrar al usuario que estaba pasando mientras el espera, bienvenidos al real de los threads, Cross THreads Exceptions fue lo que se hizo cotidiano en mi mente.

La razon de la excepciones es que no puedes accesar a objetos que no fueron creeados en este thread. Los labels del control son el MainThread y yo los intentaba accesar desde myThread lo que producía esta salida, despues de varios intentos fallidos por evitar accesar los labels y regresar información me tope con algunos NullPointer Exception resulta que la info no salía del thread, me estaba volviendo loco.

Decidí programar otras cosas y dejar el FeedHandler para despues, funcionaba despues de todo aunque se trabara la UI traia la información (bueno casi siempre :S) sin embargo mientras leía y programaba me tope con un control que no conocía el Background Worker

Resulta que este control estaba diseñado para tareas como la que yo requería, despues de pasar unos minutos leyendo la documentación y vieno los ejemplo me puse a trabajar.

Primero agregamos el control a nuestro form
Doble click lo agrega, este no es un control que se despliege como un boton aparece abajo en un cuadro gris en la vista de diseño


Una vez selecciona es muy sencillo de usar, necesitas crearle un Event Handler y poner dentro de el lo que quieres que corre en el background (en otro thread), simplemente selecciona el control, seleccionas los eventos del control (es el boton con dibujo de un rayo) y das doble click en donde dice DoWork ese sera el EventHandler para hacer el trabajo en background

Te llevara al event handler ahy agrega tu codigo que trabaje en background se ve algo así:

private void backgroundWorkerFeedAdd_DoWork(object sender, DoWorkEventArgs e)
        {
            feedManage(reporter.feedUrl);
            //Es el metodo que hace el trabajo en background
        }


Si recuerdan feedManage era el metodo que usaba para trabajar el web request, lo modifique ahora acepta un String no regresa valores pero para evitar problemas con los labels hice una pequela clase dentro de este mismo archivo ahy les va la definición solo la hice para almacenar la info y regresarla por el EventHandler de ProgressChanged del BackgroundWorker:

public class dataReporter
{
    public int percentageProgress { get; set; }
    public string reportMessage { get; set; }
    public string feedUrl { get; set; }
}


Ahora en vez de hacer doble click en DoWork (el evento del BackgrounWorker) ahora damos click en ProgressChanged y veremos algo así:

private void backgroundWorkerFeedAdd_ProgressChanged(object sender, ProgressChangedEventArgs e)
      {

      progressBar1.Step = 20; 
      //Es el porcentaje que aumenta 
      //el progressbar que ve el usuario
      
      progressBar1.PerformStep(); 
      //Le dice que aumente el % anterior
      lblProggres.Text = reporter.reportMessage;
      //El label que ve el usuario toma el valor
      //que le diga el objeto reporter este valor
      //cambia varias veces dentro del metodo feedManage(str)

      }

Dentro manejamos información del thread que trabaja, este evento se dispara al llamar al metodo backgroundWorker.ReportProgress(int); de esta manera podemos pasar información de un thread al otro sin incurrir en Cross Thread Exceptions para cambiar los valores de esta manera simplemente llamamos backgroundWorker.ReportProgress(int); cada vez que haya un cambio como esto:

reporter.reportMessage = "The url supplied is not a valid feed";
backgroundWorkerFeedAdd.ReportProgress(20);

Al llamarlo se dispara nuestro private void backgroundWorkerFeedAdd_ProgressChanged(s,e) y pasamos la información para actualizar el UI, me encanto usar este control, es extremadamente util.

Una cosa mas para correr el backgroundWorker ahy que llamarlo asi
backgroundWorker.RunWorkerAsync(args);


NOTA:Asegurate de que las propiedas WorkerReportsProgress y WorkerSupportsCancellation esten en true si planeas usarlas, yo tengo ambas en true, si no estan en true y las usas obtendras errores.


Bueno esto es todo por ahy al rato seguire posteando de lo que hago por si le sirve a alguien y si no pues para que me sirva de práctica nos vemos!

No hay comentarios:

Publicar un comentario

Deja tu comentario aquí, cuenta que te parecio el artículo o simplemente saluda!