menú
¿Música de YouTube sin molestos comerciales? ¡Ahora es posible!
TubeFiesta es una aplicación que te permite reproducir música de YouTube como un reproductor tradicional.
Crear listas, reproducción aleatoria, repetir, y hasta exportar el video a otra pantalla.
Conocer más Ir a tfiesta.com

Tutorial de Hilos en c# con ejemplos - Parte 2

Publicado el 30/12/2013 | 19779 visitas

Lleno de los mejores deseos para este año que ya casi comienza, vengo con la segunda parte del tutorial de hilos en c#. Si no viste la primera parte pues hazlo aquí.

En esta ocasión aprenderás a interactuar con la interfaz gráfica desde un hilo; comencemos.

tutorial de hilos c#

Simplemente vamos a cambiar un poco la forma en que se realizará nuestro proceso desde el hilo y mostraremos al usuario el progreso para que esté enterado y no se desespere. Pero, porque el hecho de interactuar con la interfaz gráfica desde un hilo resulta complicado?.

Cross-thread operación inválida

Este es un error muy común cuando se trabaja con hilos, pues si desde un hilo quieres actualizar algo de la interfaz gráfica, (cambiar de texto a un TextBox, habilitar un botón, quitar la visibilidad de un Label, etc.) no puedes porque estás en un contexto diferente al de la UI y obtendrás el siguiente error:

Operación no válida a través de subprocesos: Se tuvo acceso al control 'Label1' desde un subproceso distinto a aquel en que lo creó.

Es por esto que se hace "complicado" cambiar elementos visibles desde cualquier subproceso.

Hagamos que todo "reviente"

Si estás aprendiendo, no quiero darte la solución si ni siquiera conoces el problema, así que vamos a hacer que nuestro programa de Hilos nos de error y luego vamos a solucionarlo.

Vamos a modificar nuestra interfaz gráfica añadiendo un Label llamado LblProgreso y un ProgressBar llamado PrbProgreso (como está en la imagen de arriba), la idea es mostrarle al usuario el progreso mientras se está ejecutando la tarea desde el hilo.

Ahora modificaremos el código del método ya creado CorrerProceso() para que quede así:

private void CorrerProceso()
{
    CambiarProgreso("Iniciando proceso...", 0);
    //hagamos un ciclo que se repita 100 veces
    for (int i = 0; i < 100; i++)
    {
        //esperaremos medio segundo en cada iteración
        Thread.Sleep(500);
        //notificamos el avance al usuario
        CambiarProgreso(string.Format("Posición {0}...", i), i);
    }
    CambiarProgreso("Completado :D", 100);
    MessageBox.Show("Proceso finalizado");
}

Como está explicado en los comentarios repetiremos 100 veces una tarea y notificaremos al usuario el progreso por medio del método llamado CambiarProgreso(string texto, int valor), aquí está el código del método:

private void CambiarProgreso(string texto, int valor)
{
    LblProgreso.Text = texto;
    PrbProgreso.Value = valor;
}

Como pueden observar, esta es la única parte donde accedemos a controles de la interfaz gráfica, por lo tanto es donde tendremos problemas. Ahora corramos!.

Bien, si estás siguiendo el ejemplo te saldrá el siguiente error:

error a través de subprocesos

Y ahora ya conoces el problema veamos dos soluciones que lo resolverán.

Hagamos que todo funcione

La primera y NO RECOMENDADA es deshabilitar la propiedad del CheckForIllegalCrossThreadCalls así:

CheckForIllegalCrossThreadCalls = false;

No la recomiendo ya que por algo existe, y más aún, por algo, el valor por defecto es true. No explicaré para que y porque, pero si estás pensando en irte por el camino fácil te aconsejo que le des una revisada a los problemas que puede causarte esta propiedad deshabilitada aquí y aquí

Solo pon la línea de código antes de iniciar el hilo, o en el Form_Load y listo.

Claro está, es muy fácil y ahorra tiempo, se los muestro por que para hacer un ejemplo, algo rápido puede se útil, no porque sea buena practica, pero para una aplicación terminada te recomiendo la siguiente y mejor parte; utilizar delegados.

Lo que haremos es detectar si la llamada del método CambiarProgreso se está ejecutando desde un hilo, y si es así llamaremos el mismo método (CambiarProgreso) desde el contexto de la interfaz gráfica. Vamos a la practica:

Declaramos el delegado por fuera de los métodos.

delegate void CambiarProgresoDelegado(string texto, int valor);

Como lo dije antes, el delegado nos servirá para "llamar" el método CambiarProgreso, por lo tanto debe tener la misma firma (cantidad y tipos de parámetros).

Ahora modificamos el método CambiarProgreso para detectar si la llamada se está haciendo desde un subproceso o no.

private void CambiarProgreso(string texto, int valor)
{
    if (this.InvokeRequired) //preguntamos si la llamada se hace desde un hilo
    {
        //si es así entonces volvemos a llamar a CambiarProgreso pero esta vez a través del delegado

        //instanciamos el delegado indicandole el método que va a ejecutar
        CambiarProgresoDelegado delegado = new CambiarProgresoDelegado(CambiarProgreso);

        //ya que el delegado invocará a CambiarProgreso debemos indicarle los parámetros
        object[] parametros = new object[] { texto, valor };

        //invocamos el método a través del mismo contexto del formulario (this) y enviamos los parámetros
        this.Invoke(delegado, parametros);
    }
    else
    {
        //en caso contrario, se realiza el llamado a los controles
        LblProgreso.Text = texto;
        PrbProgreso.Value = valor;
    }
}

Seguramente si ejecutas así funcionará perfectamente, pero tratemos de entender más a fondo que fue lo que hicimos viendo el siguiente gráfico:

ejecución UI desde hilo

En la imagen anterior, la línea de color verde es la que se ejecuta primero, es decir, el método es llamado desde el contexto del hilo, al preguntar si this.InvokeRequired es true nos dará positivo porque la ejecución se está realizando desde el hilo (linea verde) así que ingresará e instanciará el delegado.

Luego se ejecuta el delegado con la instrucción this.Invoke() e inmediatamente se invocará nuevamente el método CambiarProgreso pero esta vez desde el contexto de la interfaz gráfica (línea roja) de modo que cuando se vuelve a evaluar la condición this.InvokeRequired ya nos dará negativo y pasará a ejecutar la tarea; cambiar el progreso.

Si aprendiste, te gustó o si tienes dudas, expresate :D; Utiliza los comentarios para hacerlo.

Igualmente no olvides compartir, recuerda que compartir es agradecer.

Hasta una próxima oportunidad y feliz año nuevo!

Talvez te interese...

Tutorial de sockets en c# con ejemplos - Parte 2 - UDP
Tutorial de sockets en c# con ejemplos - Parte 1
[solución] No puede obtener acceso a la página solicitada debido a la configuración de la extensión
[solución] El proveedor de almacenamiento especificado no se encuentra en la configuración
blog comments powered by Disqus