Capítulo 7 – Threading (Manejo de Hilos)–Parte 1

 
El concepto básico de threading es el realizar múltiples operaciones de forma concurrente. Cada una de estas operaciones puede ser realizada en un hilo separado. Es muy útil cuando se tiene computadoras con múltiples procesadores. Cuando uno no utiliza threading en una aplicación, se está desaprovechando el procesador.
Lección 1 – Creando hilos
Se encuentra en el namespace System.Threading de .Net framework y tiene los tipos necesarios para crear y administrar hilos.
Hilos simples
Métodos y propiedades más importantes de la clase Thread:
IsAlive. Indica si es hilo está ejecutándose actualmente
IsBackground. Indica si el hilo está ejecutándose como background
IsThreadPoolThread. Indica si el hilo se encuentra en el pool de hilos.
ManageThreadId. Obtiene un identificador del hilo actual.
Name. nombre asociado al hilo
Priority. Prioridad del hilo
ThreadState. Obtiene el estado del hilo
Abort. Levanta un ThreadAbortException para indicar que el hilo debe de ser abortado
Interrupt. Levanta un ThreadInterruptedException cuando un hilo está en estado bloqueado.
Join. Bloquea el actual hilo, hasta que el hilo termine.
Start. Setea el momento de ejecución.
Entre sus propiedades estáticas de Thread, estan:
CurrentContext. Obtiene el actual ThreadContext relacionado al hilo actual.
CurrentPrincipal. Obtiene el usuario asociado al hilo actual
CurrentThread Obtiene el hilo que se ejecuta actualmente.
Abort puede dejar un AppDomain con un estado no estable.
GetDomain Obtiene el AppDomain asociado con el hilo.
GetDomainId. Obtiene un identificador para el AppDomain asociado con el hilo.
ResetAbort Cancela un pedido de Abort.
Sleep Bloquea un hilo por cierto tiempo (en milisegundos)
BeginCriticalRegion Indica el inicio de una región crítica.
EndCriticalRegion Indica el fin una región crítica, donde si ocurre un Abort, puede dejar un AppDomain con un estado no estable.
ResetAbort Cancela un pedido de Abort.
 
Creando un hilo
1. Crear un método que no devuelva valor (void)
2. crear un delegado ThreadStart y especificar el método creado en el paso 1.
3. Crear un objeto Thread y especificar el objeto ThreadStart creado en el paso 2
4. Llamar a Thread.Start para empezar la ejecución del nuevo hilo
Método
image
image
Al llamar al método start , se ejecuta el método trabajo simple , que imprime por pantalla la propiedad ManagedThreadId (prop numérica a cada hilo).
image
Usando hilos multiples (Multiple Threads)
Ejemplo de creación de múltiples hilos y empezarlos a todos en un mismo trabajo.
image
Por pantalla se ven hilos consecutivos, porque el método “trabajoSimple” es muy rapido:
image
 
Usando Thread.Join
El método Join de Thread nos permite esperar por un hilo. Es decir, hay que llamar al Join del hilo que se está ejecutando, para esperarlo.
 

Thread Priority
El enumerador ThreadPriority , indica la prioridad de cada thread, tiene los siguientes valores:
Highes, AboveNormal, Normal, BelowNormal, Lowest
Normalmente, cada hilo tiene su información asociada y ésta es usualmente propagada a nuevos hilos. Esta data incluye seguridad de información, settings de localización (como el Culture del thread) e información de transacción de SystemTransactions.
 
Pasando datos a un hilo
En la realidad cuando usamos los hilos, se necesita pasar información y eso se realiza mediante el uso de un delegado llamado ParameterizedStartThread, este delegado especifica un método que recibe un parámetro object y retorna void.
image
Para utilízalo como punto de partida de una llamada a un thread se debe crear un delegado ParameterizedThreadStart que apunte al método y usar la sobrecarga del método Thread.Start que un objeto como parámetro:
image
Se debe tener cuidado de que el objeto que se le pase sea un string como se espera. Sino levantar una excepción, ejemplo:
image
Parando hilos
El mecanismo primario para parar threads es usando el método Thread.Abort.
Cuando se llama a este evento, el sistema se prepara para lanzar la excepción ThreadAbortException (se produzca o no).
image
Se debe colocar Thread.BeginCriticalRegion y Thread.EndCriticalRegion esto le indica al sistema de hilos que se puede abortar el hilo, pero NO en esa zona. El código dentro de esta región, será ejecutado como si fuese un solo hilo.

 
Ejecución de contexto
Cada hilo tiene su informacion asociada y ésta es usualmente propagada a nuevos hilos (información de seguridad, localización, de transacción (SystemTransactions).
La clase ExecutionContext brinda métodos estáticos para controlar el flujo del contexto de la información.
Se puede suprimir el contexto con ExecutionContext.SuppressFlow, esto incrementa la performance, pero se pierde seguridad, cultura e información del contexto de transacciones.
image
Para restaurar el contexto, se puede llamar ExecutionContext.RestoreFlow o flujo.Undo():
image
Se puede llamar el método Run del ExecutionContext, que permite especificar código en ese thread e información de uso. Para usar el método Run, se debe tener una copia del ExecutionContext (se realiza con ExecutionContext.Capture ).
image
 
Lección 2- Compartiendo Datos
Uno de los mayores problemas al trabajar con threads es el compartir información entre múltiples hilos.
 
Evitando Colisiones

El problema clásico del contador, es que varios hilos pueden tener un mismo valor desde memoria y luego de eso, incrementarlo y registrarlo con el mismo valor.
Al compartir información, esto se puede resolver utilizando la clase Interlocked:
Métodos:
Add. Suma 2 enteros en una operación atómica.
Decrement Resta 2 valores en una operación atómica
Exchange Intercambia 2 valores, de forma atómica.
Increment Adhiere uno a un valor de forma atómica
Read. Lee un número de forma atómica (64 bits).
 
Synchronization Locks

Permite sincronizar el acceso a los objetos en .Net. Para usarlo, se debe de colocar lock(this) en el método.
Clase:
image
Código para manejar la clase Contador2:
image
Se muestra por pantalla:
image
Para tener mucho mayor control sobre la sincronización, se puede utilizar la clase Monitor. Métodos:
Enter. Crea un lock exclusivo para un objeto especificado.
Exit. Libera el lock exclusivo para un objeto especificado.
TryEnter. Crea un lock exclusivo para un objeto especificado, y soporta timeout
Wait. Libera un exclusivo lock y bloquea el hilo actual hasta que este pueda re-adquirir el lock
Cuando se requiere una zona sincronizada, se puede modificar el código:
image
Aunque se resuelvan la mayoría de los problemas, se debe tener cuidado con los Deadlock (cuando dos piezas de código tratan de acceder al mismo objeto pero se bloquean entre ellos para obtener los recursos).
 
Otros métodos de sincronización
Además de la clase monitor, se poseen otros mecanismos de sincronización de objetos:
Clase ReadWriterLock: permite diferenciar entre las dos clases de código que usan los recursos. Bloquea el acceso a lectores y escritores por separado. Permite múltiples lectores para acceder a la información al mismo tiempo, pero solo un escritor puede obtener un lock en la info. Todos los lectores deben liberar su bloqueo antes de que un escritor pueda obtener un el suyo.
Ejemplo de cómo adquirir un reader lock:
image
Ejemplo de cómo adquirir un writer lock:
image
El ReaderWriterLock está diseñado para trabajar con las dos clases anteriores de bloqueo. También soportan pasar de un bloqueo de lectura a uno de escritura (UpgradeToWriterLock) y de este último volver al de lectura (DowngradeFromWriterLock).
El método UpgradeToWriterLock devuelve un LockCookie, es una estructura que el ReaderWriterLock utiliza para permitir el bloqueo escritor a ser bajado (downgraded) cuando se termina de escribir:
image
 
Sincronización con objetos Kernel de Windows
A nivel del sistema operativo hay 3 objetos kernel que permiten la sincronización:
  • Mutex: permite la sincronización a través de AppDomain y procesos frontera (boundaries).
  • Semáforo: es utilizado para dejar acceder a solo un número de hilos al recurso
  • Event: provee una forma de notificar a múltiples hilos que un evento ha ocurrido
Todos derivan de la clase WaitHandle, que tiene los siguientes métodos o propiedades:
Handle Obtiene o establece el objeto de manejador del sistema nativo.
Close libera todos los recursos utilizados por el actual objeto kernel
WaitOne bloquea el hilo actual hasta que el objeto kernel es señalado
 
Clase Mutex
Provee el mecanismo de bloqueo, muy parecido al Monitor, solo que tiene esa característica especial (bloquear datos a través del AppDomain).
Ejemplo:
image
Para crear un mutex con un nombre con el que puede ser llamado después, es:
image
Y para obtenerlo:
image

Clase Semaphore

Es usada para acelerar el uso de recursos. Crea un objeto kernel que soporta un numero de slots válidos.
Creando un objeto semáforo, permite especificar el número de slots usados y el máximo permitido:
image
Cuando uno desea liberar slots, se puede hacer indicando la cantidad que se desea liberar
image
Se puede usar por medio de un nombre (de la misma forma que con mutex):
image
Clase Evento (Event)
Es un tipo de objeto kernel que posee 2 estados, On y off. Esos estados permiten a los hilos esperar hasta que un evento es señalado para hacer algo.
Hay 2 tipos de eventos: auto reset y manual reset.
Cuando un evento auto reset es señalado, el primer objeto esperando por el evento se vuelve al estado no señalado. Un evento de reset manual permite a los hilos que esperan por éste para volverse desbloqueados hasta que manualmente resetee el evento a estado no señalado.
Estos eventos son: AutoResetEvent y ManualResetEvent. Ambas clases heredan de EventWaitHandle. Cuando se crea una de esas clases, se especifica el estado de la señal del evento:
image
 
Como las clases anteriores, también permite colocar un nombre al evento:
image.


 ....................................................................................................................................................
2da parte >> 
.....................................................................................................................................................

No hay comentarios:

Publicar un comentario