Formularios en hilos separados en C# y VB

Arrancar 2 formularios diferentes pero cada uno de ellos en su propio hilo utilizando clases de System.Threading que permiten la programación multiproceso en C# y en VB

Es frecuente tener una aplicación con 2 formularios en la que desde el primero en aparecer iniciamos el otro y, al cerrar el primer formulario, se cierra el segundo automáticamente. Pero en ocasiones puede interesarnos que el cierre del primer formulario no conlleve el cierre del segundo, es decir, que ambos formularios sean verdaderamente independientes y que ambos se inicien a la vez (al arrancar la aplicación). El comportamiento por defecto en las aplicaciones de Visual Studio es el comentado en primer lugar, se debe a que ambos formularios se ejecutan en el mismo hilo del proceso principal iniciado por el formulario 1 por lo que, al cerrarlo, terminamos ese hilo y todo lo que en él sucede incluido el segundo formulario.

La solución a este problema pasa por crear 2 hilos separados y ejecutar cada uno de los formularios en cada uno de los hilos, de esta manera los independizamos hasta el punto en que podemos cerrar el primer formulario y el segundo no se ve afectado. El código necesario para ello es corto y bastante sencillo:

  1. En primer lugar es necesario importar el espacio de nombres System.Threading que proporciona clases e interfaces que permiten la programación multiproceso.
  2. Después hay que crear un procedimiento Main() específico por código, en él se crean las 2 hebras o hilos diferentes apuntadas hacia 2 procedimientos también distintos encargados de arrancar cada uno de los formularios.

Para comprender mejor este ejercicio, conviene recordar algunos conceptos básicos acerca de multitarea, procesos e hilos:

  • Multithreading (Multitarea): Nombre que se le da a las arquitecturas que implementan múltiples hilos de control.
  • Thread: Concepto base de la programación multitarea. Traducido como hilo, hebra, flujo de control o contexto de ejecución dentro de un proceso. Es la unidad planificable para ejecución por la biblioteca de hilos o por el kernel.
  • Planificación: Es el proceso que consiste en decidir qué hilo se ejecutará a continuación en un determinado procesador.
  • Hilo Independiente: Es un hilo cuya finalización no es esperada por ningún otro hilo, de forma que si el hilo finaliza, es destruido inmediatamente, ya que no se necesita mantener su estado en memoria en espera de la sincronización con otro hilo.
  • Hilo Sincronizable: Es un hilo cuya finalización debe ser esperada por algún hilo (normalmente el hilo que lo creó).
  • Hilo-seguro: Normalmente se dice que un código es hilo-seguro si puede ser ejecutado en un programa multihilo sin problemas. Es decir, ese código que puede ser llamado por varios hilos al mismo tiempo.
  • Hilo-no-seguro: Es aquel código que no está preparado para ser ejecutado en una aplicación multihilo. Una solución a este problema es emplear un bloqueo global antes de llamar a este tipo de código, lo que garantiza que sólo un hilo estará ejecutando el código en un momento dado.
  • Hilos de Nivel Kernel: Son los hilos soportados por el kernel o núcleo del sistema operativo. Estos hilos son gestionados y planificados por el kernel.
  • Hilos de Nivel Usuario: Son los hilos soportados por la biblioteca o paquete de hilos, y no son conocidos por el kernel del sistema operativo.
  • Sección Crítica: Es una región de código en la que los datos a los que se accede pueden ser también accedidos por otros hilos, con lo que se pueden obtener inconsistencias en los valores de los datos. Para evitar estos problemas se suelen emplear objetos de sincronización o bloqueos.

Formulario 1 código C#

using System;
using System.Drawing;
using System.Windows.Forms;
//espacio de nombres que proporciona clases e interfaces que permiten la programación multiproceso
using System.Threading;

using Hilos_separados;

namespace Hilos_separados
{

    public class Form1 : System.Windows.Forms.Form
    {

        #region Default Instance
        #endregion

        //procedimiento Main para que la aplicación arranque desde aquí
        [STAThread()]
        public static void Main()
        {
            //crear 2 hilos diferentes, cada hilo se enlaza con un método para iniciar los formularios
            Thread hilo1 = new Thread(new System.Threading.ThreadStart(Ventana1));
            Thread hilo2 = new Thread(new System.Threading.ThreadStart(Ventana2));
            //después se arrancan los 2 hilos
            hilo1.Start();
            hilo2.Start();
        }

        #region  Código generado por el Diseñador de Windows Forms
        #endregion

        //procedimiento que muestra el primer formulario
        public static void Ventana1()
        {
            Application.Run(new Form1());
        }

        //procedimiento que muestra el segundo formulario
        public static void Ventana2()
        {
            Application.Run(new Form2());
        }

        private void Form1_Load(System.Object sender, System.EventArgs e)
        {
            //posición del formulario
            this.Top = 220;
            this.Left = 220;
        }

        //Cerrar los 2 formularios y salir de la aplicación
        private void BotonCerrar_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        //Cerrar este formulario sin salir de la aplicación.
        private void BotonCerrar2_Click(object sender, EventArgs e)
        {
            this.Close();
        }

    }

}

Formulario 2 código C#

using System;
using System.Drawing;
//using System.Diagnostics;
using System.Windows.Forms;
using System.Threading;

using Hilos_separados;

namespace Hilos_separados
{
	public class Form2 : System.Windows.Forms.Form
	{

        #region Default Instance
        #endregion

        //procedimiento Main para que la aplicación arranque desde aquí
        [STAThread()]
        public static void Main()
        {
            //crear 2 hilos diferentes, cada hilo se enlaza con un método para iniciar los formularios
            Thread hilo1 = new Thread(new System.Threading.ThreadStart(Ventana1));
            Thread hilo2 = new Thread(new System.Threading.ThreadStart(Ventana2));
            //después se arrancan los 2 hilos
            hilo1.Start();
            hilo2.Start();
        }

        #region  Código generado por el Diseñador de Windows Forms
        #endregion

   private void Form2_Load(System.Object sender, System.EventArgs e)
		{
			//posición del formulario
			this.Top = 220;
			this.Left = 220 + Form1.Default.Width;
		}
		
		//Cerrar los 2 formularios y salir de la aplicación
		private void BotonCerrar_Click(object sender, EventArgs e)
		{			
			Application.Exit();
		}
		
		//Cerrar este formulario sin salir de la aplicación.
		private void BotonCerrar2_Click(object sender, EventArgs e)
		{          
			this.Close();
		}
		
	}
	
}

Puedes descargar un archivo ZIP con las aplicaciones C# y VB.