Multiprocesos en .NET

Fecha: 26 de Mayo de 2004 (26/May/2004)
Autor: David Esteban Vergara Zapata
. jevergara@edatel.net.co

 


Resumen:
Es importante para programas más veloces el explorar la opción de utilizar multiprocesos, en especial aplicaciones que dejan la interfaz en espera de la ejecución de un proceso y le pueden hacer parecer al usuario que la aplicación ha dejado de responder. Esta es una mirada básica a este elemento tan complejo de la programación; materiales más especializados pueden ser encontrados en MSDN.


Introducción

En el desarrollo de software, siempre es un aspecto de vital importancia el explorar opciones para incrementar el rendimiento de nuestras aplicaciones; pero no tomar a la ligera una opción, sino tratar de encontrar un balance entre lo que sería lo novedoso y lo práctico. Dentro de esto encontramos los multiprocesos, los cuales pueden darle una enorme agilidad a nuestra aplicación, pero si no son aplicados adecuadamente harán que el software actúe de forma errática o sea lento. Lo que trato de decir es que (como sabemos los que estamos en este mundo maravilloso de la programación) toda técnica puede ser una espada de doble filo; puede lograr el mejor rendimiento si es aplicado debidamente o puede hacer un programa lento e inestable.


¿Qué son los multiprocesos?

Cómo ya sabemos, el sistema operativo Windows®, puede tener en ejecución múltiples programas o procesos. No significa esto que todos los procesos se ejecutan al mismo tiempo (Hablamos de una máquina con un solo procesador como son la mayoría de computadores personales), sino que el sistema operativo se encarga de dar el procesador a cada proceso de acuerdo a distintas técnicas de lo que llamamos multiplexación. Recordado esto, podemos ver a nuestra aplicación como un solo proceso; dentro del cual se encuentran agrupados la interfaz gráfica, las funciones y las peticiones externas. El problema surge cuando tenemos un programa que realiza una petición o ejecuta una función con un retardo más o menos alto que dejaría la interfaz gráfica del programa completamente estática; dando la apariencia de que el programa ha interrumpido su ejecución o no responde. En estos casos es donde debemos ver los multiprocesos como una opción debida y la plataforma .NET nos da las herramientas para utilizarlos con la mayor facilidad. Al utilizar los multiprocesos, podemos ejecutar una función sin interrumpir o bloquear la interfaz gráfica, dándole robustez a nuestras soluciones y facilitándole las cosas al usuario. Recuerdo que esto no es práctico para todos los casos así que es virtud del programador elegir cuando emplearlos.

¿Cómo funcionan?

Cuando empleamos esta técnica lo que hacemos es indicar como un proceso a una función y de acuerdo a esto, el sistema operativo le asignará el procesador, sin dejar de recibir peticiones o dar respuestas a través de la interfaz gráfica. Veámoslo como funciona la corrección ortográfica del MS Word. A medida que vamos trabajando el va subrayando aquellas palabras que no encuentra en su diccionario programado, pero no interrumpe la ejecución del programa ni nos pone en espera de realizar la acción.

¿Cómo utilizamos los multiprocesos en .NET?

Los objetos que necesitamos para utilizar los multiprocesos residen en el namespace System.Threading, donde el objeto principal sería Thread, el cual se define como un proceso. Para crear un nuevo proceso lo que hacemos es básicamente lo siguiente: Primero escribimos la función que requiere la implementación de multiprocesos, una vez sepamos cuál será la función, creamos un nuevo objeto Thread, que apunte a la dirección de la función. Se ve complicado, pero no lo es tanto; un ejemplo sería:
Supongamos que tenemos un objeto llamado Automóvil, que tiene un procedimiento llamado Arranque que interrumpiría la interfaz gráfica. También tenemos un botón de comando que iniciará el proceso. Para crear esto lo hacemos a grandes rasgos así:

Public Class Automovil
Public Sub Arranque()
'Supongamos que aquí tenemos el código
'De un procedimiento de arranque que detiene
'la interfaz gráfica
End Sub
End Class

Este sería el código dentro del botón de comando

Private Sub Boton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Boton.Click
Dim MyCar as New Automovil 'Creamos un objeto del carro
'Y de esta forma creamos el nuevo thread
Dim Proceso as New System.Threading.Thread(AddressOf MyCar.Arranque)

Proceso1.Start() 'Inicia el proceso
Proceso1.Join() 'Se debe incluir esto para esperar
'que otro proceso termine para iniciar el nuestro
End Sub

Este caso no es muy práctico, pero refleja a grandes rasgos como se manejan los Threads. Un ejemplo más práctico sería, por ejemplo para este caso, un programa que cuente las vocales de lo que escribimos a medida que lo vamos escribiendo en un Textbox, sin interrumpir la interfaz gráfica. Para esto necesitamos un Layer y un Textbox en el cual escribiremos el texto. El objetivo del programa es que a medida que se escriba se contará el número de vocales que se escriban. Para esto crearemos un objeto que tenga el procedimiento que verifica si la letra es una vocal y que guarde el texto que escribimos. El código es el siguiente:

(El objeto Textbox se llamará txtEscritura y el Label se llamará LblVocales)

Imports System.Threading
Public Class Texto
Public IsVocal As Boolean
Public Letra As String
Public Sub CheckVocal()
If Letra = "a" Or Letra = "e" Or Letra = "i" Or Letra = "o" Or Letra = "u" Then
IsVocal = True
Else
IsVocal = False
End If
End Sub
End Class
Public Class Form1
Inherits System.Windows.Forms.Form
Public Vocales As Int32
#Region " Windows Form Designer generated code "

Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.
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

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents txtEscritura As System.Windows.Forms.TextBox
Friend WithEvents lblVocales As System.Windows.Forms.Label
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.txtEscritura = New System.Windows.Forms.TextBox()
Me.lblVocales = New System.Windows.Forms.Label()
Me.SuspendLayout()
'
'txtEscritura
'
Me.txtEscritura.Anchor = (((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right)
Me.txtEscritura.Location = New System.Drawing.Point(96, 56)
Me.txtEscritura.Multiline = True
Me.txtEscritura.Name = "txtEscritura"
Me.txtEscritura.ScrollBars = System.Windows.Forms.ScrollBars.Horizontal
Me.txtEscritura.Size = New System.Drawing.Size(240, 144)
Me.txtEscritura.TabIndex = 0
Me.txtEscritura.Text = ""
'
'lblVocales
'
Me.lblVocales.Anchor = (((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right)
Me.lblVocales.Location = New System.Drawing.Point(136, 224)
Me.lblVocales.Name = "lblVocales"
Me.lblVocales.Size = New System.Drawing.Size(160, 24)
Me.lblVocales.TabIndex = 1
Me.lblVocales.Text = "Vocales:"
'
'Form1
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(424, 269)
Me.Controls.AddRange(New System.Windows.Forms.Control() {Me.lblVocales, Me.txtEscritura})
Me.Name = "Form1"
Me.Text = "Conteo de vocales"
Me.ResumeLayout(False)

End Sub

#End Region


Private Sub txtEscritura_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtEscritura.KeyPress
Dim TextoNuevo As New Texto()
Dim Tarea1 As New System.Threading.Thread(AddressOf TextoNuevo.CheckVocal)

TextoNuevo.Letra = e.KeyChar
Tarea1.Start()
Tarea1.Join()

If TextoNuevo.IsVocal = True Then
Vocales += 1
lblVocales.Text = "Vocales :" & Vocales
End If

End Sub

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

End Sub
End Class

Recomendaciones Finales

El determinar cuando emplear Threads es una tarea importante para el programador, pueden ayudar mucho o perjudicar mucho. Algunos consejos para determinar cuando usarlos y cuando no son:

- Emplear Threads cuando la tarea tardará un tiempo relativamente alto.
- Emplear Threads cuando queremos que la interfaz no se bloquee en la espera de terminar una tarea.
- No Emplear threads cuando la tarea no tardará mucho ni bloqueará la interfaz
- No Emplear threads cuando la respuesta del usuario deberá ser inmediata. Por ejemplo cuando hacemos una caja de diálogo de Login que requiere una respuesta inmediata.

Espero que este artículo sea útil para sus futuros proyectos.


ir al índice