Formularios independientes en hilos separados Fecha: 16 de Octubre de 2003 (Publicado
el 26/Oct/2003) |
. |
En ocasiones tenemos una aplicación váida con 2 formularios, desde el primero en aparecer iniciamos el otro y, al cerrar el primer formulario, se cierra el segundo automáticamente.
Pero en otras 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 descrito en el primer párrafo, habitual en las aplicaciones de Visual Basic .NET construidas por defecto, se debe a que ambos formularios se ejecutan en el mismo hilo del proceso principal por lo que, al cerrar el primer formulario, terminamos ese hilo y todo lo que en él tiene lugar, incluido el segundo formulario.
La solución a este problema pasa por crear 2 hilos separadas, y ejecutar cada uno de los formularios en cada uno de los hilos, de esta manera los independizamos hasta el punto que podemos cerrar el primer formulario y el segundo no se ve afectado.
Aunque los programadores poco expertos puedan deducir del texto anterior que debe tratarse de un código muy complejo y difícil de entender, el código necesario para ello es corto y bastante sencillo.
Para comprender mejor este ejercicio, conviene recordar algunos conceptos básicos acerca de multitarea, procesos e hilos:
- En primer lugar es necesario importar el espacio de nombres System.Threading que proporciona clases e interfaces que permiten la programación multiproceso.
- 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 tambien distintos encargados de arrancar cada uno de los formularios.
- 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, es 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.
A continuación sigue código en Visual Basic:
FORM1.VB'espacio de nombres que proporciona clases e interfaces 'que permiten la programación multiproceso Imports System.Threading Public Class Form1 Inherits System.Windows.Forms.Form 'procedimiento Main para que la aplicación arranque desde aquí <STAThread()> _ Public Shared Sub Main() 'crear 2 hilos diferentes, cada hilo se enlaza con un 'método de los que inician los formularios Dim hilo1 As New Thread(AddressOf ventana1) Dim hilo2 As New Thread(AddressOf ventana2) 'después se arrancan los 2 hilos hilo1.Start() hilo2.Start() End Sub #Region " Código generado por el Diseñador de Windows Forms " Public Sub New() MyBase.New() 'El Diseñador de Windows Forms requiere esta llamada. InitializeComponent() 'Agregar cualquier inicialización después de la llamada a InitializeComponent() End Sub 'Form reemplaza a Dispose para limpiar la lista de componentes. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Requerido por el Diseñador de Windows Forms Private components As System.ComponentModel.IContainer 'NOTA: el Diseñador de Windows Forms requiere el siguiente procedimiento 'Puede modificarse utilizando el Diseñador de Windows Forms. 'No lo modifique con el editor de código. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() ' 'Form1 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(292, 270) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D Me.Name = "Form1" Me.Text = "Form1 - Hilo 1" End Sub #End Region 'procedimiento que inicia el primer formulario Public Shared Sub ventana1() Application.Run(New Form1) End Sub 'procedimiento que inicia el segundo formulario Public Shared Sub ventana2() Application.Run(New Form2) End Sub Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'posición del formulario Me.Top = 10 Me.Left = 10 End Sub End ClassFORM2.VBPublic Class Form2 Inherits System.Windows.Forms.Form #Region " Código generado por el Diseñador de Windows Forms " Public Sub New() MyBase.New() 'El Diseñador de Windows Forms requiere esta llamada. InitializeComponent() 'Agregar cualquier inicialización después de la llamada a InitializeComponent() End Sub 'Form reemplaza a Dispose para limpiar la lista de componentes. Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() End If End If MyBase.Dispose(disposing) End Sub 'Requerido por el Diseñador de Windows Forms Private components As System.ComponentModel.IContainer 'NOTA: el Diseñador de Windows Forms requiere el siguiente procedimiento 'Puede modificarse utilizando el Diseñador de Windows Forms. 'No lo modifique con el editor de código. <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() ' 'Form2 ' Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13) Me.ClientSize = New System.Drawing.Size(292, 270) Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D Me.Name = "Form2" Me.Text = "Form2 - Hilo 2" End Sub #End Region Private Sub Form2_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load 'posición del formulario Me.Top = 10 Me.Left = 312 End Sub End ClassImagen del programa en funcionamiento:
Fichero con el código de ejemplo en Visual Basic .NET 2003 (miliuco_hebras_separadas.zip - Tamaño 25.1 KB)