Hilos en C Sharp
Hilos en C Sharp
Hilos en C Sharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
class PruebaHilos
{
static void Main()
{
Thread t = new Thread(EscribeY);
t.Start(); // Arranca EscribeY en un hilo nuevo.
while (true) Console.Write(“x”); // Escribe ‘x’ indefinidamente.
}
static void EscribeY()
{
while (true) Console.Write(“y”); // Escribe ‘y’ indefinidamente.
}
}
El hilo principal crea un nuevo hilo ‘t’ en el cual se va a ejecutar un método
que imprime el carácter ‘y’ repetidas veces en la consola. Simultáneamente, el
hilo principal hace lo mismo con el carácter ‘x’. El CLR asigna a cada hilo su
propia pila de memoria de forma que las variables locales permanecen
separadas. En el siguiente ejemplo se define un método con una variable local
y se llama al método simultáneamente tanto desde el hilo principal como del
hilo recién creado:
class PruebaHilos
{
static void Main()
{
new Thread(VamosAlla).Start(); // Llama a VamosAlla() en un nuevo hilo.
VamosAlla(); // Llama a VamosAlla() en el hilo principal.
}
static void VamosAlla()
{
// Declara y usa la variable local ciclos
for (int ciclos = 0; ciclos < 5; ciclos++) Console.Write(‘?’);
}
}
Los hilos comparten datos si tienen una referencia común a la misma instancia,
por ejemplo:
class PruebaHilos
{
bool hecho;
static void Main()
{
PruebaHilos ph = new PruebaHilos(); // Crea una instancia común.
new Thread(ph.VamosAlla).Start();
ph.VamosAlla();
}
// Date cuenta de que VamosAlla() es ahora un método de instancia.
void VamosAlla()
{
if (!hecho) { hecho = true; Console.WriteLine(“Hecho”); }
}
}
Los campos estáticos ofrecen otra manera de compartir datos entre hilos. Éste
es el mismo ejemplo con el campo “hecho” estático:
class PruebaHilos
{
static bool hecho; //Los campos estáticos son compartidos entre diferentes
hilos.
static void Main()
{
new Thread(VamosAlla).Start();
VamosAlla();
}
static void VamosAlla()
{
if (!hecho) { hecho = true; Console.WriteLine(“Hecho”); }
}
}
class PruebaHilos
{
static bool hecho;
static object bloqueador = new object();
static void Main()
{
new Thread(VamosAlla).Start();
VamosAlla();
}
// Date cuenta de que VamosAlla() es ahora un método de instancia.
static void VamosAlla()
{
lock (bloqueador)
{
if (!hecho) { Console.WriteLine(“Hecho”); hecho = true; }
}
}
}
Funcionamiento
Hilos y procesos
Todos los hilos dentro de una aplicación están dentro de un proceso. Hilos y
procesos tienen similitudes, por ejemplo, la ejecución de procesos también es
alternada con otros procesos de la misma manera que sucede con los hilos
dentro de una aplicación C#. La diferencia principal es que los procesos están
completamente aislados unos de otros, sin embargo, los hilos comparten
memoria con otros hilos que pertenecen al mismo proceso. Eso es lo que hace
que los hilos sean útiles. Un hilo puede estar generando datos en un segundo
plano mientras que otro hilo muestra los datos que van llegando.
No todo son ventajas manejando hilos. La interacción entre hilos puede ser
compleja y sin son usados en exceso o inadecuadamente, conllevan una
penalización en el rendimiento de la CPU.
Manejo de Excepciones en los hilos
namespace Pruebas
{
static class Program
{
/// <summary>
/// El punto de entrada principal de la aplicación.
/// </summary>
[STAThread]
public static void Main()
{
try
{
new Thread (Go).Start();
}
catch (Exception ex)
{
// Nosotros nunca llegaremos hasta aquí!
Console.WriteLine ("¡Excepción!");
}
}
using System;
using System.Threading;
namespace Pruebas
{
static class Program
{
/// <summary>
/// El punto de entrada principal de la aplicación.
/// </summary>
[STAThread]
public static void Main()
{
new Thread(Go).Start();
}
static void Go()
{
try
{
// Ésta excepción si que va a ser capturada abajo.
throw null;
}
catch (Exception ex) {
// Registrar de lo sucedido o informar a otros hilos
// de que nos hemos vuelto inestables.
}
}
}
}
>
Conclusión: Al menos en aplicaciones que estén ya en producción, un
manejo explícito de excepciones debe ser incorporado en todos los
métodos de entrada de todos los hilos que hayamos creado en la
aplicación. Y es que desde .NET Framework 2.0 en adelante, cualquier
excepción no manejada en cualquier hilo dará al traste con la aplicación
entera, así que ignorar esto que te estoy contando no debería ser una opción,
pero bueno, tú eliges. Además, puede ser particularmente molesto para todos
aquellos programadores Windows Forms que estén acostumbrados a usar un
manejador global de excepciones para toda la aplicación, como el siguiente:
using System;
using System.Windows.Forms;
namespace Pruebas
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.ThreadException +=
new
System.Threading.ThreadExceptionEventHandler(Application_ThreadException)
;
Application.Run(new Form1());
}
static void Application_ThreadException(object sender,
System.Threading.ThreadExceptionEventArgs e)
{
// Éste es el manejador global de excepciones de toda la aplicación.
}
}
}
Las aplicaciones Windows están basadas en eventos. Eso quiere decir que
actúan según mensajes que el sistema operativo manda al hilo principal de la
aplicación. Estos mensajes son recibidos por la aplicación mediante llamadas
repetitivas a una cola de mensajes en una sección de código llamada “Windows
message loop”. Aunque las aplicaciones .NET no requieren tener acceso directo
a este “Windows message loop”, automáticamente encaminan dichos eventos
(como las pulsaciones de teclas, ratón…) hacia sus apropiados manejadores
definidos dentro del Framework, pero en realidad, “Windows message loop”
está bajo el Framework.