3. Funcionamiento de hilos
• Todos los hilos se les asigna un tiempo de
ejecución apropiado y que todos aquellos
hilos que estén bloqueados o pausados no
consuman tiempo de CPU.
• En un ordenador con un solo procesador, un
programador de hilos gestiona una
alternancia rápida entre los hilos activos,
derivando en un comportamiento
entrecortado
• La asignación de tiempo para cada hilo está
en el orden de las decenas de milisegundos.
4. Hilos y procesos
• Todos los hilos dentro de una aplicación
están dentro de un proceso.
• La diferencia principal es que los procesos
están completamente aislados unos de otros
• los hilos comparten memoria con otros hilos
que pertenecen al mismo proceso
• Un hilo puede estar generando datos en un
segundo plano mientras que otro hilo
muestra los datos que van llegando.
5. Procesos y subprocesos
• Para que un proceso sea capaz de hacer
• algo, el proceso debe ser propietario de un hilo
(thread).
• Este thread es el responsable de ejecutar el
código contenido en el espacio de direcciones del
proceso.
• un proceso puede contener varios threads y todos
ellos ejecutando código “simultáneamente "en el
espacio de direcciones del proceso
6. • cada thread tiene su propia colección de
registros de la CPU y su propio stack (pila de
almacenamiento)
• threads se ejecutan concurrentemente al
asignar ‘rodajas de tiempo’ (time slices)
llamadas quantums a cada thread
alternativamente (y consecutivamente).
• cada quantum es de 15 milissegundos.
7. Administración de procesos
• Process es un componente que nos permite
• Iniciar
• detener
• controlar
• supervisar aplicaciones.
8. Administración de hebras y hilos
• El ámbito System.Threading proporciona en
la plataforma .NET las clases e interfaces
para escribir código multihebra.
• Cada hebra con un proceso
• Si el proceso crea más hebras, será
multihebra(multithreading)
9. Por que son útiles las hebras
• En primer lugar, cuando hay una tarea de
fondo. Imagine que quiere poner un logotipo
que gire en la parte superior izquierda del
formulario.
• En segundo lugar, cuando esta realizando
más de una tarea a la vez Podría dividir la
imagen en cuatro partes y ejecutar las
cuatro copias en la rutina de procesado de
imagen
10. La clase Thread
PROPIEDADES
• CurrentPrincipal
• CurrentThread
• IsAlive
• IsBackground
• Name
• Priority
• ThreadState
12. Control de las hebras
• Llame a su método Start() para comenzar la
ejecución
• Terminar una hebra puede llamar al método
Abort().
• Los métodos Suspend() y Resume() se pueden
utilizar para parar temporalmente la ejecución
de una hebra y a continuación reiniciarla de nuevo
• Sleep() se utiliza para poner una hebra a dormir
durante un periodo de tiempo, normalmente
especificado como un número de milisegundos.
13. Estados y prioridad de las hebras
La enumeración ThreadState, describe los posibles
estados que puede tener una hebra:
Elementos
• Aborted
• AbortRequested
• Background
• Running
• Stopped
• StopRequested
• Suspended
• SuspendRequested
• Unstarted
• WaitSleepJoin
14. • Un elemento de la enumeración ThreadPriority,
cuyos valores son:
Elemento
• Hightest
• AboveNormal
• Normal.
• BelowNormal
• Lowest
15. Sincronización de hebras
• compartidos y por la temporización.
• cada función de la hebra tiene su propio
conjunto de variables locales, puesto que las
variables locales se declaran en la pila
• variables locales no pueden interferir con
otras, porque se
• crean en diferentes pilas.
16. Las clases sin sincronización
• La clase Interlocked contiene cuatro métodos de
hebra segura compartidos para realizar
operaciones con variables
• estos métodos son atómicos, por lo que no se
pueden
• interrumpir por cambios de contexto de hebras:
• Increment: Incrementa una variable.
• · Decrement: Disminuye una variable.
• · Exchange: Pone una variable a un valor y
devuelve el valor original.
• · CompareExchange: Compara dos valores y
reemplaza el valor destino si son iguales
17. ¿Qué es multithreading?
• El multithreading es manejado internamente por
un programador de threads, una función que el
CLR típicamente delega el sistema operativo.
• Un programador de threads se asegura que
todos los threads activos se encuentren
apropiadamente dispuestos en tiempo de
ejecución, y que los threads que se encuentran
bloqueados - por ejemplo por un bloqueo
exclusivo, o esperando una entrada del usuario -
no consuman recursos de tiempo del CPU.
18. Threads vs. Procesos
• Todos los threads de una aplicación se
encuentran contenidos en proceso – la unidad
del sistema operativo en la cual una aplicación
corre.
• Los threads tiene ciertas similitudes con los
procesos – por ejemplo los procesos corren bajo
time-sliced con otros procesos en la
computadora de un modo similar a los threads
en una aplicación C#.
19. • La principal diferencia es que los procesos se
encuentran totalmente aislados de otros
procesos, mientras que los threads comparten el
montículo de memoria (heap) con otros threads
de la misma aplicación.
• Esto hace que los threads sean muy útiles: un
thread puede recuperar datos en segundo plano,
mientras que otro thread va mostrando los datos
mientras llegan.
20. ¿Cuándo utilizar Threads?
• Una aplicación común para el multithreading es
la ejecución en segundo plano de tareas que
consumen mucho tiempo. El thread principal se
mantiene corriendo mientras que el thread que
realiza la operación funciona en segundo plano.
• En aplicaciones Windows Forms, si el thread
principal realiza operaciones largas, los mensajes
del teclado y el mouse no pueden ser
procesados, como resultado la aplicación deja de
responder.
21. • Por esta razón es bueno hacer que las tareas que
insumen mucho tiempo funcionen en threads de
trabajo y en todo caso el thread principal puede
mostrar el diálogo modal “Procesando… espere
por favor” en caso que el programa no pueda
continuar hasta que la tarea termine.
• Esto asegura que la aplicación no sea tomada
por el sistema operativo como "No responde"
incitando al usuario a forzar la finalización del
proceso!
• El diálogo modal permite implementar un botón
"Cancelar" ya que el diálogo continúa recibiendo
eventos mientras que la tarea es realizada por el
hilo trabajador.
22. • En el caso de las aplicaciones in interfaz gráfica,
como los servicios de Windows, el
multithreading toma particular importancia
cuando una tarea es potencialmente
consumidora de tiempo porque se encuentra a la
espera de la respuesta de otra computadora
(como un servidor de aplicaciones, un servidor
de base de datos, o un cliente).
• Tener un thread trabajador realizando dicha
tarea significa que el thread que lo instancia se
encontrará inmediatamente libre para hacer
otras cosas.
23. • Otra aplicación del multithreading es utilizarlo en
métodos que realizan cálculos intensivos. Tales
métodos pueden ejecutarse más rápido en
computadoras con múltiples procesadores si la
carga de trabajo es dividida en múltiples threads.
• La cantidad de procesadores puede obtenerse
por medio de la propiedad
Environment.ProcessorCount.
24. • Una aplicación C# puede convertirse en multi
thread de dos maneras:
• Creando explícitamente threads adicionales, o
utilizando características del framework .NET
que implícitamente crean threads – como
BackgroundWorker, thread pooling, un threading
timer, un Remoting server, o un Web Services o
una aplicación ASP.NET.
• En estos últimos casos no se tiene control del
multithreading.
25. ¿Cuándo no utilizar Threads?
• El multithreading posee desventajas. La más
grande es que implica mayor complejidad en los
programas. La creación de una aplicación
multiples threads no es compleja, la complejidad
se encuentra en la interacción entre los threads.
• Esto es así cuando la interacción es válida o no, y
puede derivar en largos procesos de desarrollo y
la consecuente susceptibilidad de intermitentes
y difícilmente reproducibles bugs.
26. • Por esta razón es recomendable mantener el
diseño de la interacción entre los múltiples
threads simple – o no utilizar multithreadingl – a
menos que tangas una peculiar inclinación por
re-ecribir y debuggear!
27. • El multithreading también implica consumo de
recursos y costo de CPU en crear y cambiar entre
threads si es utilzado en exceso.
• En particular, cuando implica pesados procesos
de lectura/escritura a disco, puede ser más
rápido tener uno o dos threads trabajadores
realizando las tareas en secuencia en lugar de
tener múltiples threads trabajando en
simultáneo.
28. Creando e iniciando Threads
• Los threads utilizando el constructor de la clase
Thread, pasándole un delegado del tipo
ThreadStart– indicando el método desde donde
debe iniciar la ejecución. Aquí vemos la definición
del delegado ThreadStart:
• public delegate void ThreadStart();
• Llamando al método Start logramos que la ejecución
comience. El thread continúa hasta que su método
retorne, en este punto el thread finaliza.
29. • Aquí vemos un ejemplo utilizando la sintaxis larga
de C# para crear el delegado TheadStart:
• class ThreadTest
{
static void Main()
{
Thread t = new Thread (new ThreadStart (Go));
t.Start();
new thread.Go(); // Corre
simultaneamente Go() en el thread principal.
}
static void Go()
{
Console.WriteLine ("hello!");
}
30. • Se puede crear un thread de un modo más
conveniente utilizando la sintaxis corta de C#
para instanciar delegados:
• static void Main()
{
Thread t = new Thread (Go); // No es
necesario utilizar explícitamente ThreadStart
t.Start();
...
}
static void Go()
{
...
}
31. • En este caso el delegado ThreadStart es inferido por el
compilador. Otro método es utilizar un método anónimo
para iniciar un thread:
• static void Main()
{
Thread t = new Thread (delegate()
{
Console.WriteLine ("Hello!");
}
);
t.Start();
}
• Un thread tiene su propiedad IsAlive en valor true
después de llamar a su método Start(), hasta que el
thread finaliza. Una vez finalizado un thread no puede
ser re-iniciado.
32. Pasando datos a ThreadStart
• En ejemplo de arriba queremos distinguir mejor
la salida de cada thread, por ejemplo haciendo
que uno de ellos imprima en mayúscula.
• Podemos lograr esto pasando un flag al método
Go: pero no podemos usar el delegado
ThreadStart porque no acepta argumentos.
Afortunadamente en framework .NET define otra
versión del delegado llamada
ParameterizedThreadStart, la cual acepta un
objeto como parámetro del siguiente modo:
33. • public delegate void ParameterizedThreadStart (object obj);
• Entonces el ejemplo previo se ve así:
• class ThreadTest
{
static void Main()
{
Thread t = new Thread (Go);
t.Start (true);
Go (true)
Go (false);
}
static void Go (object upperCase)
{
bool upper = (bool) upperCase;
Console.WriteLine (upper ? "HELLO!" : "hello!");
}
}
•
hello!
HELLO!
34. • En este ejemplo el compilador infiere automáticamente el
delegado ParameterizedThreadStart porque el método Go
acepta un objeto como argumento.
• Podríamos haberlo escrito:
Thread t = new Thread (new ParameterizedThreadStart (Go));
t.Start (true);
35. Nombrando Threads
• Se puede asignar nombre a un thread a través de
su propiedad Name. Esto es de gran beneficio en
la depuración: y también permite hacer
Console.WriteLine nombre del thread, Microsoft
Visual Studio recoge el nombre del thread y lo
muestra en la barra de herramientas Debug
Location.
• Podemos nombrar un thread en cualquier
momento, pero sólo una vez, en caso de intentar
cambiar el nombre de un thread obtendremos
como resultado una excepción.
36. • También podemos asignarle nombre al thread principal de la aplicación,
en el siguiente ejemplo hacemos esto a través de la propiedad estática
CurrentThread:
class ThreadNaming
{
static void Main()
{
Thread.CurrentThread.Name = "main";
Thread worker = new Thread (Go);
worker.Name = "worker";
worker.Start(); Go();
}
static void Go()
{
Console.WriteLine ("Hello from " + Thread.CurrentThread.Name);
}
}
•
Hello from main
Hello from worker
37. Threads en primer y segundo
plano
• Por defecto los threads corren en primer plano,
esto quiere decir que la aplicación siguirá
corriendo mientras que alguno de sus threas se
encuentre activo.
• C# soporta threads en segundo plano
(background threads) los cuales no mantiene la
aplicación corriendo por si mismo, sino que
terminan inmediatamente cuando la aplicación
finaliza.
38. • Cambiar un thread de primer a segundo plano
no cambiar su estado o prioridad en la
planificación del CPU de ninguna manera.
La propiedad IsBackground del thread controla el
modo de ejecución como en el siguiente
ejemplo:
39. • class PriorityTest
{
static void Main (string[] args)
{
Thread worker = new Thread (delegate() {
Console.ReadLine();
});
if (args.Length > 0) worker.IsBackground = true;
worker.Start();
}
}
40. • Si llamamos al programa sin pasarle argumentos
el thread de trabajo corre en su modo por
defecto de primer plano y esperará en la
instrucción ReadLine hasta que el usuario
presione Enter. Mientras tanto el thread
principal deja de existir pero la aplicación sigue
corriendo porque existe un thread en primer
plano todabía vivo.
• Por otro lado, si un argumento es pasado al
thread principal (Main()) el thread de trabajo es
ejecutado en segundo plano y el programa deja
de existir cuando el thread principal termina,
finalizando la instrucción ReadLine.
41. • Cuando un hilo en segundo plano termina de
este modo, todos los bloques finally son
ignorados. Ya que no es deseable ignorar los
bloques finally es una buena práctica esperar a
que todos los threads de trabajo terminen antes
de finalizar la aplicación – posiblemente con un
timeout (esto puede lograrse utilizando
Thread.Join). Si por alguna razón un thread
renegado nunca termina, podemos intentar
abortarlo, y si esto falla, abandonar el thread
permitiendo que muera con el proceso.
42. • Tener threads en segundo plano puede ser
beneficioso, por la razón que es posible que el
último diga cuando la aplicación debe terminar.
Consideremos la alternativa - un thread en
primer plano que no muere - evitando que la
aplicación termine. Un thread en primer plano
olvidado es particularmente dañino en
aplicaciones Windows Form, porque la
aplicación aparentará terminar (el menos para el
usuario) pero el proceso continuará corriendo.
43. Preguntas…
• ¿Qué es un proceso y subproceso?
• ¿Qué es un thread?
• ¿Cuál es la diferencia entre estos dos?
• ¿Qué librería se utiliza para poder usar threads?
• ¿Cuáles son algunas de las propiedades de la clase thread?
• ¿Cuándo utilizamos los threads?
• ¿Cuándo no es necesario utilizar threads?
• ¿Qué es multithreading?
• ¿Cuáles son algunos estados y prioridades de los threads?
• Cuando creamos un thread ¿Qué tiempo es asignado para
cada hilo?