ArcanuS Web Blog Imagen Del Blog

Hilos y Multitarea en C#

marzo 16th, 2008 · 59 Comentarios

CHola a todos. Hoy vamos a tratar un tema referente al desarrollo de software. Es algo con lo que muchos se vuelven locos y algo que muchos otros piensan que es complicadísimo de entender e implementar, y peor aun, existen “programadores” que hasta creen que es algo innecesario.

Estoy hablando nada mas y nada menos que de “Threading” o (en criollo) “Hilos”.
La implementación de hilos en nuestras aplicaciones, mejorarán el rendimiento y la organización a bajo nivel de las mismas.
Un Hilo, no es mas que un Sub Proceso. Por tanto, delegar un procedimiento o una función a un hilo, no es mas que hacerla correr como un Sub Proceso del Proceso principal, que generalmente es el main de la aplicación.
La ventaja principal de trabajar de esta manera, es poder tener “Cosas” corriendo en BackGround. Esto es, tener por ejemplo un procedimiento corriendo, y poder seguir usando el programa principal de forma normal. Esto último lo van a entender bien cuando hagamos la práctica.
Dada esta introducción adentrémonos en el tema.

El lenguaje elegido esta vez es C#.net porque me parece que ya es hora de aprender un lenguaje complejo (dije complejo, no difícil) y altamente funcional.
Vamos a hacer una aplicación que cree por código múltiples barras de progreso y las valla llenando según lo indique una función.
Abrimos el Visual Studio y creamos un nuevo proyecto C# Aplicación de Windows.
En el formulario que se nos crea por defecto solo agregamos un botón en una esquina y ajustamos el tamaño de esta manera:

Captura1

Ahora, escribimos el código del formulario para que quede así:

namespace EjemploHilos1
{
public partial class Form1 : Form
{
int Posicion = 0;

public Form1()
{
InitializeComponent();

}

private void Form1_Load(object sender, EventArgs e)
{

}

private void button1_Click(object sender, EventArgs e)
{
Posicion = Posicion + 30;

CrearProgressBars(23,Posicion,23,200);
}

private void CrearProgressBars(int sX, int sY, int sAltura, int sAncho)
{
ProgressBar pb = new ProgressBar();
int ContarHasta = new Random().Next(100000, 5000000);

pb.SetBounds(sX, sY, sAncho, sAltura);
pb.Parent = this;
pb.CreateControl();

for (int contador=1; contador <= 100; contador++)
{
pb.Value = contador;

for (int CuentaNumeros = 0; CuentaNumeros <= ContarHasta ; CuentaNumeros++) ;
}

}

}

}

Veamos la explicación:
Primero declaramos la variable Posicion y la inicializamos en cero. Esta variable se encargará de llevar la coordenada de altura que tendrá la nueva barra de progreso. Sin esta variable, todas las barras se crearían una arriba de la otra.

public Form1()
{
InitializeComponent();
}

Estas líneas son propias de Visual Studio y se encargan de inicializar todos los componentes del formulario.

{
Posicion = Posicion + 30;

CrearProgressBars(23,Posicion,23,200);
}

Acá veremos el evento Click del botón. Acá hacemos que la variable global Posicion se incremente en 30. Las barras de progreso las crearemos con una altura de 23 unidades, por eso la nueva posición va a ser 30 unidades por debajo de la anterior barra (23 que ocupa la barra anterior y 7 de separación así no quedan pegaditas ^_^ ). Después llamamos a la función CrearProgressBars() y le pasamos por valor las coordenadas de posicionamiento en el form mas el ancho y el alto.

private void CrearProgressBars(int sX, int sY, int sAltura, int sAncho)
{
ProgressBar pb = new ProgressBar();
int ContarHasta = new Random().Next(100000, 5000000);

pb.SetBounds(sX, sY, sAncho, sAltura);
pb.Parent = this;
pb.CreateControl();

for (int contador=1; contador <= 100; contador++)
{
pb.Value = contador;

for (int CuentaNumeros = 0; CuentaNumeros <= ContarHasta ; CuentaNumeros++) ;
}

}

Esta es nuestra función encargada de crear las barras. La función recibe los parámetros sX (coordenada X), sY (coordenadaY), sAltura y sAncho, todos de tipo entero.
Primero y principal declaramos e instanciamos pb de modo que nos quede un nuevo objeto de tipo ProgressBar. Luego utilizamos la propiedad SetBounds para establecer el posicionamiento en el formulario y le pasamos los parámetros de posicionamiento recibidos.
También declaramos la variable ContarHasta de tipo entero y le asignamos un número al azar entre cien mil y cinco millones.
Ahora le decimos a la propiedad Parent que el contenedor padre del control es este mismo formulario (pb.Parent = this;), y por último llamamos al método CreateControl() para que al fin nos cree el control en el formulario.

Lo siguiente es generar el retardo suficiente para llenar la barra de progreso. Hay muchas formas de hacer esto. Yo elegí hacer que el programa cuente de 0 hasta un número aleatorio alto (como mínimo 100 mil y como máximo 5 millones en mi caso), acción que según la capacidad de procesamiento de mi pobre computadora, es suficiente para que el proceso sea visible al ojo humano ejeje. Si ustedes tienen computadoras mas rápidas, aumenten esta cantidad hasta que se ajuste a sus requerimientos.

Para esto hice dos bucles For. el primero declara una variable contador de tipo entero, la inicializa en uno y se va a repetir hasta que la variable llegue a 100 incrementándola de uno en uno. En cada repetición aumenta el valor de la propiedad Value de la barra, (que por defecto es cero) en 1, (Esto es lo que hará que vallan pasando las rayitas en la barra ^_^). El segundo bucle For es el que hace el trabajo sucio. Declara la variable CuentaNumeros de tipo entero, la inicializa en cero y se va a repetir hasta que la variable llegue al valor aleatorio que tomó la variable ContarHasta al principio de la función.

Bien, es hora de ejecutar el programa y ver los flamantes resultados. Ejecutemos y presionemos varias veces sobre el botón para ver que pasa.

Captura2

Y???, ¿cómo les fue? Chan Chan!. La aplicación funciona, pero de una manera “poco ética” diría Richard Stallman. Si se fijaron bien, por mas que ustedes hacían repetidos clics sobre el botón, la aplicación estaba como trabada, y solo creaba la nueva barra una vez que la anterior terminaba de llenarse y no instantáneamente cuando ustedes hacían el clic. Si no te diste cuenta, ejecutá de nuevo el programa y volvé a probar.
Esto sucede porque todo corre en un solo proceso: el del programa en si. Por tanto, el proceso está activo, pero trabajando. Es por eso que perdemos el control de la aplicación. solo cuando la función CreateProgressBars() finaliza, el programa retorna el control al usuario.
Este mismo escenario, trasladado al desarrollo real se suele ver muy frecuentemente en aplicaciones que utilizan sockets o que realizan grandes transacciones a servidores de bases de datos en donde los tiempos de red no son siempre los que uno quisiera. La mayoría de estos casos a gran escala suelen terminar en un cuelgue generalizado de la aplicación y la perdida de información que esto conlleva.
Para evitar todos estos dolores de cabeza es que se usan los Hilos. Y como dijo un buen amigo, “El buen manejo de hilos es lo que separa a un Programador de un Aprendiz de coder”.
Quiero aclarar que esto de los hilos no es nada nuevo, ni tampoco es nada que Microsoft descubrió y que va a salvar al mundo ni nada de eso. La gente que programa en C o algunos de C++, vienen oyendo palabras como Hilos, sub procesos, delegados, etc desde hace mucho tiempo.
Lo novedoso, si se quiere, son las clases que el framework.net ofrece para el manejo de hilos, invokes y delegados. Están muy pulidas y constan de una gran abstracción, lo que facilita mucho el trabajo y el entendimiento de los mismos.
Todas las clases y SubClases referentes a hilos se encuentran en el espacio de nombres System.Threading, por lo tanto debemos importarlo al principio del código.

using System.Threading;

El FrameWork, también maneja los procesos y subProcesos de una forma especial, y no como simples procesos del sistema. Digo especial porque constan de restricciones de seguridad para evitar un mal uso de la sintaxis como sería puntear algo fuera de la memoria reservada del framework o acceder a un objeto situado en un proceso diferente. Pero bueno mejor no sigo que no quiero que se mareen. Sigamos. Lo único que me interesa que sepan es que para el FrameWork, los procesos “Desprendidos” del proceso principal son SubProcesos del mismo, (y no procesos independientes), y el sistema está enterado de esto. Cuando el proceso principal muera, morirán también todos sus SubProcesos, por mas que se encuentren en el medio de la ejecución. Y si queda algo en memoria, ya se encargará el GarbageCollector. Ahora si, sigamos ejeje.

Ustedes me van a putear, pero después de pensarlo mucho decidí no usar el código antes visto para seguir explicando el uso de hilos, ya que decidí no meterme en temas muy profundos como son los delegados, los handlers y los eventos porque ya se perdería el enfoque de este tutorial. Mejor vamos a hacer un código nuevo y ver bien gráficamente el trabajo con hilos. Mejor usemos el código anterior para ver lo que sucede con un programa normal cuando ejecutamos múltiples acciones sobre el mismo proceso (se cuelga).
Lo que sucede es lo siguiente. Las barras de progreso son creadas desde el proceso principal, pero para ir llenándose necesitan ser accedidas desde el hilo que se encargue de esa tarea. Ahora el problema está en que a partir del framework 3.0 un objeto solo puede ser accedido solo desde el mismo proceso que lo creó. Por tanto habría que crear un delegado que obtenga los datos del estado de la barra, se los pase a un handler y este los traiga a una función dentro del proceso principal que se encargue de actualizar la barra. No es que sea algo del otro mundo, pero si es algo demasiado avanzado para este tutorial, pero lo dejo pendiente para los próximos!!!

Bueno, vamos a crear una nueva aplicación de consola. Una vez que la tengamos le escribimos el siguiente código:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace EjemploHilos
{
class Program
{
static void Main(string[] args)
{
Proceso oProceso = new Proceso();

Console.WriteLine(“Contando PARES en el proceso principal: “);
Console.WriteLine(“—————————————”);
Console.WriteLine();

oProceso.ContarNumerosPares();

Console.WriteLine(“Contando IMPARES en el proceso principal: “);
Console.WriteLine(“—————————————–”);
Console.WriteLine();

oProceso.ContarNumerosImpares();

Console.WriteLine(“Contando PARES desde un hilo: “);
Console.WriteLine(“—————————–”);
Console.WriteLine();

Thread HiloPar = new Thread(oProceso.ContarNumerosPares);
HiloPar.IsBackground = true;
HiloPar.Start();
HiloPar.Join();

Console.WriteLine(“Contando IMPARES desde un hilo: “);
Console.WriteLine(“——————————-”);
Console.WriteLine();

Thread HiloImpar = new Thread(oProceso.ContarNumerosImpares);
HiloImpar.IsBackground = true;
HiloImpar.Start();
HiloImpar.Join();

Console.WriteLine(“Lanzando los dos hilos al mismo tiempo: “);
Console.WriteLine(“—————————————”);
Console.WriteLine();

Thread nuevoHiloPar = new Thread(oProceso.ContarNumerosPares);
Thread nuevoHiloImpar = new Thread(oProceso.ContarNumerosImpares);

nuevoHiloPar.Start();
nuevoHiloImpar.Start();

Console.ReadLine();
}
}

class Proceso
{
public void ContarNumerosPares()
{
for (int numero = 0; numero <= 100; numero = numero + 2)
{
Console.Write(numero + ” “);
for (int GranContador = 0; GranContador <= 20000000; GranContador++) ;
}

Console.WriteLine();
Console.WriteLine();
}

public void ContarNumerosImpares()
{
for (int numero = 1; numero <= 100; numero = numero + 2)
{
Console.Write(numero + ” “);
for (int GranContador = 0; GranContador <= 20000000; GranContador++) ;
}

Console.WriteLine();
Console.WriteLine();
}
}
}

Bueno, desarmemos esto. Fíjense que tenemos creadas dos clases: La principal (class Program) y la clase Proceso (class Proceso).

Proceso oProceso = new Proceso();

Console.WriteLine(“Contando PARES en el proceso principal: “);
Console.WriteLine(“—————————————”);
Console.WriteLine();

oProceso.ContarNumerosPares();

Primero creo un objeto llamado oProceso y lo instancio. Las siguientes 3 líneas escriben en pantalla lo que está entre comillas. Por último lanzo el método ContarNumerosPares(). Aquí estaremos lanzando el método desde el proceso principal.

Console.WriteLine(“Contando IMPARES en el proceso principal: “);
Console.WriteLine(“—————————————–”);
Console.WriteLine();
oProceso.ContarNumerosImpares();

Lo mismo que antes pero contando números impares.

Console.WriteLine(“Contando PARES desde un hilo: “);
Console.WriteLine(“—————————–”);
Console.WriteLine();

Thread HiloPar = new Thread(oProceso.ContarNumerosPares);
HiloPar.IsBackground = true;
HiloPar.Start();
HiloPar.Join();

Acá es donde comienza la fiesta. Primero volvemos a escribir en pantalla las cadenas de texto. Ahora procedemos a declarar e instanciar el hilo, llamado HiloPar. Para esto utilizo la clase System.Threading.Thread. Fíjense que cuando la instancio le paso por parámetro el nombre del método que voy a lanzar en el hilo. En Vb.net es lo mismo solo que le paso la dirección de memoria del método en cuestión usando AddressOf. Luego establezco la propiedad IsBackground a True. Esto hace que el hilo pase a trabajar en segundo plano. Después lanzo el hilo (HiloPar.Start()). El método Start, permite pasar un parámetro siempre y cuando este sea de tipo object. Podemos pasar un numero por ejemplo pero de tipo objeto, que luego habrá que castear a su tipo dentro del hilo. Pasar parámetros a un hilo es algo que también veremos en próximos tutoriales. Finalmente, y con el hilo ya lanzado, ejecuto el método Join(). Esta acción hace que no se devuelva el control al proceso principal, hasta que el sub proceso termine. Después prueben ejecutar el programa comentando todas las líneas Join() para que vean lo que sucede.

Console.WriteLine(“Contando IMPARES desde un hilo: “);
Console.WriteLine(“——————————-”);
Console.WriteLine();

Thread HiloImpar = new Thread(oProceso.ContarNumerosImpares);
HiloImpar.IsBackground = true;
HiloImpar.Start();
HiloImpar.Join();

Lo mismo que antes pero para el método ContarNumerosImpares().

Console.WriteLine(“Lanzando los dos hilos al mismo tiempo: “);
Console.WriteLine(“—————————————”);
Console.WriteLine();

Thread nuevoHiloPar = new Thread(oProceso.ContarNumerosPares);
Thread nuevoHiloImpar = new Thread(oProceso.ContarNumerosImpares);

nuevoHiloPar.Start();
nuevoHiloImpar.Start();

Console.ReadLine();

Solo nos queda desarmar la clase Proceso. Veámosla de nuevo:

class Proceso
{
public void ContarNumerosPares()
{
for (int numero = 0; numero <= 100; numero = numero + 2)
{
Console.Write(numero + ” “);
for (int GranContador = 0; GranContador <= 20000000; GranContador++) ;
}

Console.WriteLine();
Console.WriteLine();
}

Acá definimos el método ContarNumerosPares(). Dentro de el tenemos nuestros queridos bucles For. Al igual que en el código anterior, uno cuenta de 0 a 100 y en cada repetición escribe el valor en pantalla y después ejecuta al bucle que hace el trabajo sucio: Contar de 0 a 20000000. De esta forma relentizamos el programa así podemos verlo. De lo contrario no alcanzaríamos a ver nada, salvo que estuviéramos con una commodore64, pero si estuviéramos con una commodore64 no estaríamos programando en C#.net, ufffff ya se me saltó la chapa. (Ida de bola de ArcanuS …….). Mejor sigamos.
Las dos líneas Console.WriteLine(); escriben un salto de línea en la consola.

public void ContarNumerosImpares()
{
for (int numero = 1; numero <= 100; numero = numero + 2)
{
Console.Write(numero + ” “);
for (int GranContador = 0; GranContador <= 20000000; GranContador++) ;
}

Console.WriteLine();
Console.WriteLine();
}
}

Lo mismo, solo que es el método ContarNumerosImpares().

Ahora vamos a lanzar los dos hilos al mismo tiempo y veremos como es que funciona esto de la multitarea. volvemos a crear 2 hilos nuevos apuntando a los métodos ContarNumerosPares() y ContarNumerosImpares(). Te preguntarás por qué volvemos a crear los hilos si ya están creados e instanciados. Como estamos en una aplicación de consola, la ejecución aquí es puramente lineal, y como algunos sabrán, un proceso una vez que muere no se puede reiniciar. Una vez muerto se descarga de la memoria y hasta la vista proceso. Si estuviéramos en una aplicación Windows Forms, y tuviéramos la creación de los hilos dentro del evento click de algún botón, no habría problema con usar los mismos hilos, ya que cada vez que se presione el botón se estarían creando nuevos hilos (por tanto en realidad no estaríamos usando “los mismos hilos” [pequeños engaños del framework xDDD]). En fin, volvemos a crear e instanciar los hilos. Luego los lanzamos uno atrás de otro. Por último escribimos Console.ReadLine(); para que la consola no se cierre hasta que no presionemos una tecla.
Ahora si, sentate, ponete cómodo, preparate el mate y ejecutá el programa. La salida debería ser similar a esta:

Captura3

Y con esto, estaríamos terminando este tutorial sobre hilos y multitarea en C#.net, no sin antes aclarar algunas cosillas que quedaron en el tintero.
La MultiTarea que obtenemos en este caso no es una Multitarea real, al menos en mi caso ya que tengo una computadora con un solo procesador. Salvo que ustedes tengan una maquina con mas de un procesador, obtendrán una multitarea simulada.
Permítanme aquí hablar un poquito de un tema avanzado pero que es bueno saberlo. Quiero aclarar que para lo próximo que van a leer, me basé en las opiniones vertidas por Tom Archer en su libro “A fondo C#” de Microsoft Press.

Multitarea y Alternancia de Contexto (Context Switching):

Al principio de este turorial dijimos que un Hilo es simplemente un subproceso de un proceso principal o creador. Permítanme ahora, que sabemos un poco mas del tema, modificar un poco esta definición.
Un hilo de ejecución es una unidad de procesamiento, y la multitarea consiste en la ejecución de múltiples hilos en simultaneo. Esta posee dos ramas principales: Multitarea Cooperativa y Multitarea Preferente.
Si recordamos versiones antiguas de Windows como Windows 3.11, veremos que este poseía Multitarea Cooperativa, en donde cada hilo de ejecución era el responsable de renunciar al control del procesador para que este pudiera atender a otros hilos. En mi experiencia personal, la primera vez en mi vida que vi multitarea preferente fue hace mucho tiempo, cuando tenia al rededor de 8 o 9 años y empecé a usar OS/2 y a hacer mis primeras aplicaciones en REXX (Lenguaje de scripting del sistema os/2). Después ya vino Windows NT, 95, 98, 2000, etc que traían la misma multitarea preferente de Os/2 (raro Microsoft afanando algo no?).
Con la multitarea preferente, el procesador es el encargado de asignar al proceso una cierta cantidad de tiempo en la que ejecutarse. Un ‘quantum’ diría mi profesor de Procesamiento Distribuido, pero yo, porfiado como soy, prefiero llamarle TimeSlice.
Entonces, el procesador se encargará de darle a cada proceso su timeslice de forma alternativa, así el programador no tiene que preocuparse por cuando recibe el control el proceso y cuando lo pierde ni de cuando ceder el control para que el resto de los hilos puedan ejecutarse. Vale aclarar que .NET solo puede ejecutarse en sistemas con multitarea preferente. Creo que ya me entendieron por qué hablo de una multitarea “simulada”. Si nuestra aplicación corre sobre una maquina con un solo procesador, este está simplemente alternando los hilos en fracciones de milisegundos, por lo cual tenemos una sensación de multitarea. Si queremos tener una multitarea de verdad, necesitaremos desarrollar y correr nuestra aplicación en una maquina con múltiples procesadores.

La Alternancia De Contexto es algo que siempre está ahí, pero que no se ve, y a su vez es un concepto un poco difícil de asimilar para algunos.
El procesador, utiliza un temporizador de hardware para saber cuando ha terminado una fracción de tiempo (TimeSlice) para un proceso determinado. Cuando el temporizador lanza la interrupción, el procesador salva en la pila el estado de los registros del hilo de ejecución en curso. Una vez hecho esto, mueve esos datos a una estructura de datos denominada estructura CONTEXT. Cuando el procesador quiere retornar a un hilo de ejecución previamente ejecutado, deshace la operación anterior y recupera los valores existentes en la estructura CONTEXT, depositándolos nuevamente en los registros. El conjunto de todas estas operaciones se llama: Alternancia de Contexto.

En fin, no sigo mas porque si no los voy a matar del aburrimiento. Con esto me despido y espero que este tutorial le halla servido a alguien. Por favor, dejen sus comentarios en este post.
Para este tutorial que me llevó dos días armar, quiero agradecer a dos personas en especial.
Gracias a Néstor V. Un compañero de trabajo de mi madre quien inconscientemente sembró en mi la curiosidad por la programación. fue él además quien me prestó el CD de Visual Basic 5 para que yo lo instale en mi computadora y comience a jugar con el lenguaje a la edad de mas o menos 12 años (allá por el año 1998).
La segunda persona a la que quiero agradecer es a Melina T. fue mi profesora de programación en lenguaje Vb 6 del único curso de programación que tomé en mi vida. Fue por el año 2000 mas o menos. Melina fue como una musa inspiradora, (hablando siempre de programación, aunque también era muy muy linda), durante los 11 meses que duró el curso. Gracias Meli por ayudarme a sentar las bases en ese lenguaje y en mi cabeza, bases validas para todo tipo de lenguajes, y por enseñarme a razonar de forma lógica y matemática.

Solo resta agradecerles a ustedes por dejarme expresarme en estos tutoriales. El código fuente de los dos ejemplos lo dejo acá. Y también dejo la versión en pdf de este tutorial (La cual está bien formateada y tabulada).

Hasta la próxima.

by ArcanuS

Tags: Programación y Desarrollo

votar

Dejame Tu Comentario:

59 comentarios en este artículo ↓

  • 1 Minipost: Manual de hilos y multitarea en C# | Blog de Tinchio // mar 16, 2008 a las 4:25

    [...] publicó en su blog un manual, de su autoria, donde da un pantallazo y una buena introducción al tema de hilos y multitarea en [...]

  • 2 plukazz // mar 16, 2008 a las 10:19

    buena ayuda!
    para cuando web services en c#??

  • 3 ArcanuS // mar 17, 2008 a las 0:12

    Prometido para proximos tutoriales!

  • 4 MCH // mar 30, 2008 a las 14:29

    Muy buen tutorial…

    Y ademas con c sharp, me gusta mucho ese lenguaje…

    Saludos…

  • 5 Sergio Boudy // abr 8, 2008 a las 4:40

    Hola
    esta muy bueno tu manual, y realmente me sirvió de mucho para entender como funciona la multitarea, pero yo estoy haciendo un proyecto en el que tengo que utilizar la parte e la que no quisiste llegar, que es dentro de un hilo tener varios hilos corriendo y saber desde la forma en que estado se encuentra. Comentaste que creando un delegado que le pace los valores a un handler. Bueno si me pudieras ayudar te lo voy a agradecer.
    Saludos Sergio.

  • 6 rogelio // abr 21, 2008 a las 2:44

    muchisimas gracia de verdad no ecuentro palabras para agredecerte por este tutorial q…… me a salvado la vida……gracias ati estoy a poco de terminar mi proyecto.

  • 7 ArcanuS // abr 21, 2008 a las 2:58

    Rogelio, la alegria es doble ya que pone muy contento que te halla servido el tutorial. Gracias a ti por pasarte por mi web. Bienvenido y cualquier cosa que necesites me avisas.
    Saludos!

  • 8 rogelio // abr 21, 2008 a las 3:08

    (ya lo corri paso por paso y lo que me da problemas es el que cuando se ejecuta el hilo de enemigo,fcarrito o carreterarayas estos se ejecutan al mismo tiempo y estoy pensando si puedo ponerle tiempo al joind para que termine primero con el fcarrito o el enemigo o el carretera rayas PD: mi nombre es rogelio amaya y mi equipo pedro aragon y riky m.cuen por si se le ocurre al profe pasarse por aqui decirnos que de aqui lo copiamos)
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading;
    using xCon;

    namespace carritoultimate
    {
    class Program
    {
    static int renglon=10,columna=27,I,J,Rencon;
    static ConsoleKeyInfo movimiento;
    static void Main(string[] args)
    {
    Thread Hilo0 = new Thread(new ThreadStart(contrarios));
    Thread Hilo = new Thread(new ThreadStart(carrito));
    Thread Hilo1 = new Thread(new ThreadStart(carretera));
    xConsole.SetColor(xCon.ConsoleColor.WhiteForte);
    xConsole.Locate(6, 1);
    Console.Write(“████████████████████████████████████████████████████████████████████████████████”);
    xConsole.Locate(18, 1);
    Console.Write(“████████████████████████████████████████████████████████████████████████████████”);
    Hilo.Start();
    Hilo0.Start();
    Hilo1.Start();
    }
    static void carrito()
    {
    do
    {
    movimiento = Console.ReadKey(true);
    Thread Hiloc = new Thread(new ThreadStart(fcarrito));
    Hiloc.IsBackground = true;
    Hiloc.Start();
    Hiloc.Join();
    if (movimiento.Key == ConsoleKey.RightArrow)
    {
    columna++;
    if (columna >= 37)
    {
    columna = 37;
    }
    }
    else
    {
    if (movimiento.Key == ConsoleKey.LeftArrow)
    {
    columna–;
    if (columna <= 27)
    {
    columna = 27;
    }
    }
    else
    {
    if (movimiento.Key == ConsoleKey.UpArrow)
    {
    renglon–;
    if (renglon 15)
    {
    renglon=15;
    }
    }
    }
    }
    }
    }
    while (movimiento.Key != ConsoleKey.Escape);
    }
    static void fcarrito()
    {
    xConsole.Locate(renglon, columna);
    xConsole.Locate(renglon – 2, columna);
    Console.Write(” “);
    xConsole.Locate(renglon – 1, columna);
    Console.Write(” ☺ ☺ “);
    xConsole.Locate(renglon, columna);
    Console.Write(” ████■ “);
    xConsole.Locate(renglon + 1, columna);
    Console.Write(” ☺ ☺ “);
    xConsole.Locate(renglon + 2, columna);
    Console.Write(” “);
    }
    static void carretera()
    {
    for (int T = 1; T 0; I–)
    {
    Thread Hilo3 = new Thread(new ThreadStart(carreterarayas));
    Hilo3.IsBackground = true;
    Hilo3.Start();
    Hilo3.Join();
    }
    xConsole.ClearScreen();
    xConsole.Locate(6, 1);
    Console.Write(“████████████████████████████████████████████████████████████████████████████████”);
    xConsole.Locate(18, 1);
    Console.Write(“████████████████████████████████████████████████████████████████████████████████”);
    }
    }
    static void contrarios()
    {
    Rencon = 9;
    for (J = 60; J >= 0; J–)
    {
    if (Rencon == 9)
    {
    if (J == 0)
    {
    J = 60;
    Rencon = 15;
    }
    }
    if (Rencon == 15)
    {
    if (J == 0)
    {
    J = 60;
    Rencon = 9;
    }
    }
    Thread Hilo2 = new Thread(new ThreadStart(enemigo));
    Hilo2.IsBackground = true;
    Hilo2.Start();
    Hilo2.Join();
    }
    }
    static void enemigo()
    {
    Thread.Sleep(30);
    xConsole.Locate(7, 1);
    xConsole.Locate(Rencon, J);
    Console.Write(“▀▀█████ “);
    xConsole.Locate(Rencon – 1, J);
    Console.Write(” ▄▄ “);
    xConsole.Locate(Rencon + 1, J);
    Console.Write(“☼☼☼☼☼☼ “);
    fcarrito();
    }
    static void carreterarayas()
    {
    Thread.Sleep(200);
    xConsole.Locate(12, I);
    Console.Write(“████████████████████ ████████████████████ ████████████████████ “);
    xConsole.Locate(13, 1);
    Console.Write(” “);
    xConsole.Locate(7, 1);
    Console.Write(” \n \n \n \n \n \n \n \n”);
    }
    }
    }

  • 9 rogelio // abr 21, 2008 a las 3:09

    por sierto si se te hace muy grande lo puedes eliminar solo lo puse como ejemplo y grac por tu ayuda

  • 10 rogelio // abr 24, 2008 a las 19:08

    bueno pues muchas gracias otra ves ya terminamos el juego lo entregamos asi pero de forma que se vea decente al menos aaun que no por esto creas que no estoy esperando tu respuesta ya que aunque hayamos terminado mi quipo y yo queremos aprender a dominar los thread (hilos) ya que como dijiste si eres unh buen programador no tienes que sacarles la vuelta sino dominarlos.
    PD: sigo esperando respuesta y muchas gracias de nuevo =) mi equipo y yo te agradesemos por este articulo publicado aqui.

  • 11 Liliana // abr 29, 2008 a las 11:55

    hola
    yo necesito desarrolar un software que te permita ver los programas que se encuentren activos en un computador con c#
    agradeceria me ayuden con este problema o me orienten un poco….
    gracias

  • 12 Lili // may 4, 2008 a las 13:27

    Olis ^^

    Oie super …esta super la explicacion ademas no me aburri para nada.. ojala pongas mas explicaciones sobre las barras ese esta super interesante eh!!
    Si puedes mandarme un correo .. a mi igual me gusta programar ^^

    Cuidate muchoo….

    bueno bueno me despido byebye

  • 13 RenePmmer // may 5, 2008 a las 0:16

    Good Job, buena forma de comenzar el trabajo con hilos pero me quede ansioso por mas… Salu2

  • 14 Diego // jun 1, 2008 a las 15:24

    Hola, esta muy bueno este tuto, pues mi interes es hacia la barra, mi pregunta es, como hago para hacer q despues de q la barra se llene por completo desaparesca de inmediato?, se q no tiene q ver con el tema, pero si puede ayudarme, o alguien q sepa (drog_4@hot)
    gracias

  • 15 javier z // ago 28, 2008 a las 13:46

    Muy buen tutorial. Soy nuevo en esto de la programacion y me interesa mucho aprender. Gracias por la informacion,muy bien explicada

  • 16 Dubhe // oct 6, 2008 a las 16:11

    Buen tutorial para empezar lo veo muy explicativo para empezar, Gracias
    PD:
    Espero los proximos tutoriales

  • 17 casmol // dic 25, 2008 a las 23:04

    gracias por el tutorial… y si paso lo que dijiste cuando cambiaste el programa, haha… tambien yo espero los proximos tut…

  • 18 pepos // feb 1, 2009 a las 17:47

    gracias por el trabajo, me sirvio bastante para entender los hilos =)!

  • 19 Luis // mar 7, 2009 a las 1:46

    OYe rogelio trate de ejecutar tu programa por k se me Hizo muy bueno y lo kiero ver como se ejecuta…. me marca un error en xCon.

  • 20 Luis // mar 7, 2009 a las 1:54

    Es ke hace falta una Referencia pero no se como ponerla….

  • 21 09Itachi09 // mar 8, 2009 a las 21:18

    gracias bicho muy bueno
    si tienes algo de hilos en c, no en c#,
    te agradecería que me lo envies
    a mi correo
    nuevamente gracias

  • 22 Mateo // jun 17, 2009 a las 18:13

    Muy bueno el tutorial, me será de gran ayuda.

  • 23 robert // mar 28, 2010 a las 3:22

    hola .. beuno muchas gracias estoy realiando un proramita usando hilos socket y esas copsas pero gracias por la explicacion me ayuda de mucho a comprender mejor esto de los hilis …gracias

  • 24 ArcanuS // mar 30, 2010 a las 1:12

    De nada robert, me alegro que te halla gustado! Saludos.-

  • 25 7H3_7HR1LL3R // abr 30, 2010 a las 17:59

    Esta muy bueno el totorial, pero me dejaste con las ganas de conocer acerca de lo que no quisiste mencionar de los delegados, para cuando publicaras ese articulo???

  • 26 Sanu // may 13, 2010 a las 11:56

    Exelente tiene una manera bastante clara de como generar Hilos, me sirvio mucho =)

  • 27 Mauro R. // may 17, 2010 a las 22:24

    Alguien sabe como puedo enviar parametros a un hilo

  • 28 Yolanda // may 22, 2010 a las 15:27

    Buenisimo este tutorial.
    Me ha venido muy bien para sentar algunos conceptos.
    Ojalá saques pronto ese del que hablas sobre delegados y handlers. Si es asi no me lo perderé.
    Enhorabuena.

  • 29 Jaime Zevallos // jul 16, 2010 a las 14:24

    Muy buen tutorial.
    Gracias.
    Ya estaba cansado de programar en VB 6.0, ya que no es multi hilos.
    Nuevamente Gracias.

  • 30 MANUCHAO // ago 17, 2010 a las 12:38

    Te agradesco por el tutorial y por tu tiempo para compartir tus conocieminetos, el tuto fue lo bastante claro y puntual.
    Por favor si tuviese un tutorial sobre DELEGADOS , HANDLERS y EVENTOS me seria muy util, que segun expones en este tutorial conoces bastante al respecto.
    Una vez mas muchas gracias y sigue compartiendo tus conocimientos….

  • 31 Pierina // sep 7, 2010 a las 10:54

    Hola. En el manual hablas de algo sobre delegado y handler, no has realizado mas nada sobre esto? gracias!

  • 32 Ivan // nov 20, 2010 a las 19:30

    Hola, Excelente Tutorial, Muchas felicidades.

    Una ptición, ojalá pudieras profundizar un poco mas sobre el asunto del ejemplo de los progress bar, sobre esto de que un sub proceso no puede acceder a los objetos creados por otro subproceso, y cómo resolverlos a través de delegados… eso me interesa mucho mucho
    (^-^)

    Saludos

  • 33 Julio Vera // abr 26, 2011 a las 22:15

    Que onda! pues nada mas para agradecerte por el tutorial, ando en esto de la programación especialmente en c# (por el momento). La verdad muy bueno que le hayas dedicado el tiempo para escribir esto, me sirve bastante, además me agrada la manera en como lo explicas, yo hubiera seguido leyend de las multitareas…. ahi estamos al pendiente.. saludos

  • 34 ArcanuS // may 24, 2011 a las 18:26

    Hola Julio, muchisimas gracias por tus palabras. Un placer que estés entre nosotros! Abrazo!

  • 35 Miguel Rodriguez // jun 2, 2011 a las 19:24

    excelente ejemplo man!!!
    se te agradece por compartirlo XD

  • 36 ArcanuS // jun 2, 2011 a las 20:04

    Te agradezco a vos por leerlo! Saludos!

  • 37 Escuelante // jun 24, 2011 a las 13:32

    Muchas gracias por el tutorial, es excelente y aporta mucho, sobre todo a los que iniciamos en la programación. Saludos desde Pachuca Hgo México.
    Gracias!

  • 38 omar simon // jul 19, 2011 a las 15:11

    muchas gracias x tu aporte de verdad k me sirvio mucho hace tiempo y se me olvido comentarte pero bn muchas gracias x k esa ves me salvaste la vida y de nuevo gracias

  • 39 yoelvys // jul 27, 2011 a las 12:56

    simplemente excelente, es primera vez que trabajo con hilos, mira tengo una duda, tengo una aplicacion en C# pero no de consola sino de form y tengo dentro del metodo que llama el hilo un acceso a un richTextBox y me dice que estoy accediendo a un componente que creo otro subproceso en este caso el principal, si me puedes ayudar te lo agradecere.
    saludos

  • 40 ArcanuS // jul 27, 2011 a las 15:24

    Hola yoelvis. Gracias por comentar. Te cuento que no podes acceder directamente desde un subproceso a otro porque eso implicaría una falla de seguridad, por tanto el framework no lo permite. Tenes que hacerlo mediante un manejador. Te dejo un link al respecto en donde se trata el tema.
    Espero que te sirva y me comprometo a hacer una segunda parte de este tutorial, abarcando ya temas mas profundos relacionado al trabajo con hilos y multitarea.
    Saludos.-

  • 41 ArcanuS // jul 27, 2011 a las 15:28

    Muchas gracias a vos por pasar y comentar!

  • 42 Daniel // ago 21, 2011 a las 1:33

    gracias me ha serivido de mucho… si puedes con el de delegados handlers y eventos seria super bueno…

  • 43 David // sep 26, 2011 a las 0:41

    Necesito un programa con dos hilos, que me traigan información de un archivo plano (txt) y me muestre el nombre y luego el apellido de cada persona que esta en el archivo.

    Gracias por la ayuda…

  • 44 Diego Rojas // sep 26, 2011 a las 14:44

    Excelente tutorial…. ya q la clave para aprender es ir de lo simple a lo complicado..

    Gracias

  • 45 JNR // sep 26, 2011 a las 19:13

    Hola muy buen tutorial. necesito hacer un proyecto donde se apliquen los conceptos de concurrencia, sincronizacion, interbloqueo e inanicion, con los procesos que quiera. Creo que usar hilos de ejecucion es la mejor manera. Necesito que me orienten, ALGUIEN QUE ME AYUDE ES URGENTE. muchas gracias por la ayuda que me puedan brindar

  • 46 JNR // sep 26, 2011 a las 19:15

    por cierto C# creo es el lenguaje que necesitare gracias.

  • 47 Sergio Paz // oct 9, 2011 a las 15:02

    Hola!
    muy buen post me sirvio de mucho
    Gracias!!!

  • 48 miguell // oct 25, 2011 a las 0:45

    solo me sirvio lo de la barra mejora un poco tus tutos

  • 49 Jessica Vega // nov 2, 2011 a las 16:02

    Esta super tu programa me gusto mucho lo hice y me corrio perfecto :D estoy precisamente en este tema en la materia de Topicos Avanzados de Programacion y tambien estamos viendo eventos controlados por hilos o al rrevez no recuerdo jaja :D super ojala pudieras hacer uno de esos n.n

  • 50 Antonio // nov 9, 2011 a las 0:19

    hola muchas garcias por tu tutorial mesirvio mucho es deomemejores nada aburrido muy bien explicado saludos megusto mucho saludos de de puebla mexico

  • 51 memox // nov 20, 2011 a las 21:02

    Oye muy buena explicación , sin embargo soy uno de los que te quiere putear XD hahaha. La verdad me interesa mucho como usarlo con esos progressbars y soy algo nuevo en esto, me podrias ayudar porfavor?? ya vi lo de los delegados pero eso del event handling no se bien que onda. Gracias

  • 52 Edwar // dic 1, 2011 a las 11:54

    excelente… entretenido con los comentarios extras.

  • 53 mario // dic 22, 2011 a las 15:50

    hola buenas tardes gracias por el tutorial me sirvio bastante, actualmente desarrollo una aplicacion web en c# y tengo 3 timer uno que agrega lista a cola, otro que regfresca mi grid en tiempo real y uno mas que recupera los datos de cola, mi apliacion en algunos casos se lega a trabar yo pienso que es por el choque de los hilos cree un hilo para acceder a mi metodo de recuperar elemento de la cola pero me marca error con unas variables de seesion ya que mi grid lo lleno en tiempo de ejecucion dejo mi correo sica1@live.com.mx. gracias

  • 54 comentario // ene 16, 2012 a las 19:52

    malisima la explicacion, y 10 veces peor el codigo

  • 55 Abel // may 9, 2012 a las 11:55

    Muy bueno; se le entiende poco mas que lo que esplica el profe. jaja haber si pones uno mas corto pa irle agarando la onda de como se usan los hilos.

  • 56 Augusto // nov 23, 2012 a las 17:35

    Excelente tutorial!!!! Gracias!!1

  • 57 Gordita // dic 12, 2012 a las 22:30

    hola… Gracias muy bueno el tutorial, pero me quedo una duda si yo quisiera imprimir esto en Windows Form lo tendria q hacer en un MessageBox, o tendria q colocar en el formulario un TextBox o algun otro…?????

  • 58 Yelinna // ene 18, 2013 a las 15:08

    Gracias por explicar cómo se administran los hilos a nivel procesador. Siempre es bueno saber qué ocurre “allá abajo”

  • 59 gregory // feb 20, 2013 a las 10:57

    Muy bueno…