Jul 22

Manejo de hilos en C# (parte 1) + delegates + invoke

Etiquetas: C# Versión para imprimir Pablo

En el artículo que explica la implementación del patrón Singleton en C# utilizamos un método que inicializa 42 hilos los cuales modifican un atributo del objeto Singleton. Además de eso, cuando se lanza el evento que “avisa” de la modificación del atributo, se muestra el valor en una label del form, por lo cual deberemos usar delegados para evitar un problema de CrossThreadException.

Primero veamos el código que crea los threads:

private void button2_Click(object sender, EventArgs e)
        {
            System.Threading.ThreadStart ts = new System.Threading.ThreadStart(sumarRandomico);
 
            Int16 i;
            for (i = 0; i <= 42; i++)
            { 
                System.Threading.Thread t = new System.Threading.Thread(ts);
                t.IsBackground = true;
                t.Name = String.Format("Thread_{0}", i.ToString());
                t.Start();
                System.Diagnostics.Trace.WriteLine(String.Format("Thread {0} >>> Contador: {1} <<<", t.Name, objetoSingleton.Instancia.Contador.ToString()));
            }
        }

En la primera linea creamos el ThreadStart, que es el que indica el método que se ejecutará en el thread, luego dentro del bucle for creamos el objeto thread, seteamos que se ejecute en background, le seteamos un nombre y luego iniciamos la ejecución del hilo. Hasta ahí no hay misterio, simplemente instanciamos e iniciamos varios hilos, en caso de que el método que se ejecutará en el nuevo hilo requiera parámetros, utilizaremos un ParameterizedThreadStart que veremos en la segunda parte de este artículo.

Ahora veamos cómo mostrar en pantalla el valor evitando las CrossThreadException, las mismas suceden cuando se intenta acceder a un objeto de un hilo, (la interfaz en este caso), desde otro hilo.
Cuando se intenta acceder a un control desde otro hilo, la propiedad InvokeRequired toma el valor true, por lo que consultando el valor de esta propiedad sabremos si podemos acceder directamente al control o no.

void Instancia_CambioContador(long nuevoValor)
        {
            if (!this.label2.InvokeRequired)
            {
                CambiarValorLabel(nuevoValor.ToString());
            }
            else
            {
                CambiarValorDelegate del = new CambiarValorDelegate(CambiarValorLabel);
                this.label2.Invoke(del,nuevoValor.ToString());
            }
        }
 
        private delegate void CambiarValorDelegate(String nuevoValor);
        private void CambiarValorLabel(String nuevoValor)
        {
            this.label2.Text = nuevoValor;
        }

Como vemos, si InvokeRequired es false, llamamos directamente al método que modifica el Text de nuestra label, de lo contrario necesitaremos crear un delegate para ese método y utilizarlo con el método Invoke del control.
Un delegate es, a grandes rasgos, un puntero, lo cual en este caso evita la CrossThreadException porque accede directamente a la dirección de memoria del método que llamaremos. Para utilizar un delegate, tenemos que definirlo primero y éste debe tener la misma firma que el método que llamará.

Al momento de utilizarlo, instanciamos nuestro delegate y utilizamos el método Invoke de nuestra label:

CambiarValorDelegate del = new CambiarValorDelegate(CambiarValorLabel);
this.label2.Invoke(del,nuevoValor.ToString());

También te puede interesar:

Comentarios[5]

  1. Smack3rNo Gravatar

    creo que con ese ejemplo, el uso de los delegados se explica solo,
    muy bueno

  2. PabloNo Gravatar

    Gracias Smack3r, me alegra ver que los ejemplos están sirviendo!!

  3. faboNo Gravatar

    Pot favor necesito saber como le asigno el delegado a mi picture box
    ya esta hecha toda la programación pero todo esta con string en tu ejms.

  4. PabloNo Gravatar

    @fabo, es lo mismo, te creas un método, por ejemplo void SetPicture(Bitmap pic) y un delegado con la misma firma, en definitiva, lo único que cambia es el tipo del argumento y a qué control le cambias el valor. Espero que te sirva esta ayuda, sino utiliza el form de contacto y me das más detalles de lo que necesitas.
    Saludos!

  5. MauricioNo Gravatar

    Excelente ejemplo. A veces es complicado encontrar ejemplos simples, y este realmente explica perfectamente la idea de delegados, y en pocas lineas!!!
    El ejemplo del patron Observer tambien es perfecto. Realmente los felicito.
    Saludos!

Deja un comentario