Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

jueves, 9 de noviembre de 2023

Hilos con C#, corregido

Continuando con mi entrada anterior, el detalle que hay que corregir es el siguiente: no se puede modificar la interfaz gráfica desde otro hilo. Las interfaces gráficas de .Net y Java no son thread-safe, incluso estos frameworks arrojarán una excepción si detectan que se está intentando modificar la interfaz gráfica desde otro hilo que no haya sido el que la creó.

Como ejemplo, voy a añadir un par de líneas al final de las funciones Hilo1() e Hilo2() en el código que dejé como ejemplo:






En muchas aplicaciones que modifican la interfaz gráfica es necesario llamar a las funciones Refresh() de los controles. Después de este cambio, la aplicación arroja esta excepción:



La manera correcta, en C#, de modificar la interfaz gráfica desde otro hilo es usando las funciones Invoke() o BeginInvoke() de la plataforma .Net. 

Por otro lado, es una mala práctica llamar a la función Abort() de un hilo. Si se desea que un hilo finalice junto a la aplicación que lo creó se debe declarar como hilo de background, eso se hace en una línea de código:

p2.IsBackground = true;

El código corregido es:

using System;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Threading; 


namespace WindowsApplication1

{

    public partial class Form1 : Form

    {

        Thread p1;

        Thread p2;

        byte r, g;

        bool b1, b2;

        

        public Form1()

        {

            InitializeComponent();

        }


        private void Form1_Load(object sender, EventArgs e)

        {

            r = 0; g = 255; b1 = false; b2 = true; 


            p1 = new Thread(new ThreadStart(Hilo1));

            p2 = new Thread(new ThreadStart(Hilo2));


            p1.IsBackground = true;

            p2.IsBackground = true;


            p1.Start();

            p2.Start(); 

        }

        

        public void Hilo1() {

            while (true)

            {


                Thread.Sleep(10);


                if (r >= 0 && r <= 255 && b1 == false)

                {

                    r++;

                    if (r == 255)

                        b1 = true;

                }


                if (r >= 0 && r <= 255 && b1 == true)

                {

                    r--;

                    if (r == 0)

                        b1 = false;

                }


                if (this.InvokeRequired)

                {

                    this.BeginInvoke((Action)(() =>

                    {

                        pictureBox1.BackColor = Color.FromArgb(r, 80, 100);

                        pictureBox1.Refresh();

                    }));

                }

            }

       }


        public void Hilo2() {

            while (true)

            {

                Thread.Sleep(10);


                if (g >= 0 && g <= 255 && b2 == false)

                {

                    g++;


                    if (g == 255)

                        b2 = true;

                }


                if (g >= 0 && g <= 255 && b2 == true)

                {

                    g--;


                    if (g == 0)

                        b2 = false;

                }


                if (this.InvokeRequired)

                {

                    this.BeginInvoke((Action)(() =>

                    {

                        pictureBox2.BackColor = Color.FromArgb(100, g, 80);

                        pictureBox2.Refresh();

                    }));

                }

            }

        }


        private void Form1_FormClosed(object sender, FormClosedEventArgs e)

        {

        }

    }

}

 

El nuevo ejemplo se puede descargar de aquí.