38
Juan Camilo Jiménez Dorado [email protected] Diciembre 2011 Procesos e hilos

Juan Camilo Jiménez Dorado [email protected] Diciembre 2011

Embed Size (px)

Citation preview

Page 1: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Juan Camilo Jiménez Dorado [email protected]

Diciembre 2011

Procesos e hilos

Page 2: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Instrucciones

Estado

Memoria de trabajo

Información de planificación

ProcesosEjecución de un programa compuesto por:

Page 3: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Estados de un proceso

Unidad mínima de asignación de recursos: Proceso (Tarea).

Page 4: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

HilosSub rutinas ejecutándose dentro de un proceso.

Se usan cuando:

El hilo principal no es suficientemente veloz.

Se necesita procesamiento asincrónico.

Se necesita procesamiento en paralelo.

Se va a realizar procesamiento en batch.

Se van a recibir peticiones simultaneas. (como lo hacen ASP.NET, WCF, Web Services, or Remoting)

Page 5: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Estados de un Hilo

Unidad mínima de expedición de recursos: Hilo. (comparten recursos)

Page 6: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Estados de un HiloEstado DescripciónRunning El subproceso se ha iniciado, no está bloqueado y no existe una excepción 

ThreadAbortException pendiente.StopRequested Se ha solicitado que el subproceso se detenga. Esto último sólo se refiere al uso

interno.SuspendRequested El subproceso se ha marcado para suspensión.Background El subproceso está ejecutándose como subproceso en segundo plano, por oposición

a un subproceso en primer plano. Para controlar este estado, hay que establecer la propiedad Thread.IsBackground.

Unstarted No se ha invocado al método Thread.Start en el subproceso.

Stopped El subproceso se ha detenido.WaitSleepJoin Subproceso bloqueado. Este podría ser el resultado de llamar a Thread.Sleep o 

Thread.Join, de solicitar un bloqueo (por ejemplo, llamando a Monitor.Enter o Monitor.Wait) o de esperar en un objeto de sincronización de subprocesos como ManualResetEvent.

Suspended El subproceso se ha suspendido.AbortRequested Se ha invocado al método Thread.Abort en el subproceso, pero el subproceso aún no

ha recibido la excepciónSystem.Threading.ThreadAbortException pendiente que intentará finalizarlo.

Aborted El estado del subproceso incluye AbortRequested y el subproceso está ahora inactivo, pero su estado no ha cambiado todavía aStopped.

Page 7: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Estados de un HiloAcción ThreadState

Se crea un subproceso dentro de Common Language Runtime. Unstarted

Otro subproceso llama al método Start del subproceso. Unstarted

El subproceso comienza a ejecutarse. Running

El subproceso llama a Sleep. WaitSleepJoin

El subproceso llama a Wait o realiza alguna otra llamada de bloqueo. WaitSleepJoin

El subproceso llama a Join en otro subproceso. WaitSleepJoin

Common Language Runtime marca el subproceso de suspensión, por ejemplo, para realizar la recolección de elementos no utilizados.

SuspendRequested

Subproceso suspendido. Suspended

El subproceso se reanuda después de que haya sido suspendido. Running

Otro subproceso llama a Thread.Abort en el subproceso. AbortRequested

Subproceso finalizado. Stopped

El propio subproceso u otro ha establecido la propiedad Thread.IsBackground del subproceso en true.

Background

Page 8: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Estados de un Hilo

Page 9: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Se tarda mucho menos tiempo en crear un hilo nuevo en un proceso existente que en crear un proceso.

Se tarda mucho menos en terminar un hilo que un proceso.

Se tarda mucho menos tiempo en cambiar entre dos hilos de un mismo proceso.

Los hilos aumentan la eficiencia de la comunicación entre programas en ejecución.

Multithreading vs. Procesos

Page 10: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Desventajas de los hilos

Incrementan la complejidad de la aplicación.

No siempre a mayor número de hilos mayor performance. El número incorrecto de hilos puede perjudicar el rendimiento.

En operaciones de escritura de disco, muchos hilos perjudican el porformance.

Page 11: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Punto de entrada de un proceso. Puede dar inicio a los demás hilos

Hilo principal

Page 12: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Ámbito de un hiloclass Program{  

static void Main()   {    

Thread t = new Thread (WriteY);          // Crear un nuevo hilo secundario   

t.Start();                               // Iniciar hilo secundario  // Imprimir X en el hilo principal    for (int i = 0; i < 1000; i++) Console.Write ("x");  

}  

static void WriteY()   {    

// Imprimir Y en el hilo secundariofor (int i = 0; i < 1000; i++) Console.Write ("y");  

} }

Page 13: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Hilos compartiendo datospublic static void main(){

new Thread(Go).Start(); // Llama GoShare() en un nuevo hiloo

Go(); // Llama GoShare() en el hilo principal} static void Go(){

// Imprime ? 5 veces cada que lo llamenfor (int cycles = 0; cycles < 5; cycles++) Console.Write('?');

}

bool done;public static void main(){

ShareSameInstance tt = new ShareSameInstance(); // Crea una instancia común de la clase programnew Thread(tt.Go).Start();tt.Go();

}

// Go() pasa a ser un método de instanciavoid Go(){

if (!done) { done = true; Console.WriteLine("Done"); }}

Nota: Tambien se pueden datos a través de variables estáticas

Page 14: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

JoinEspera hasta que un hilo termine para continuar con la ejecución.

static void main(){

Thread t = new Thread(Go);t.Start();t.Join();Console.WriteLine("El hilo ha terminado!");

}

static void Go(){

for (int i = 0; i < 1000; i++) Console.Write("y");}

Se puede usar un TimeSpan para elegir un time out

t.Join(TimeSpan.FromSeconds(2));

Page 15: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Sleep

Nota: Mientras se usa Sleep o Join, el hilo está bloqueado y no consume recursos de CPU

Suspende un hilo por un tiempo determinado

Thread.Sleep(TimeSpan.FromMinutes(1));

Page 16: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Interrupt / AbortInterrupt desbloquea hilos previamente bloqueados, mientras que abort los interrupe así no estén bloqueados.

Interrupt espera hasta el próximo bloqueo del hilo para ejecutar las demás instrucciones.

Nota: la operación abort es insegura en la mayoría de escenarios, por ejemplo, cuando se está escribiendo en disco.

public static void main(){

Thread t = new Thread(Go);t.Start();t.Abort();

} static void Go(){

while (true){

// Imprime X hasta que se cancela

Console.Write('X');}

}

public static void main(){ var cancelSource = new CancellationTokenSource(); new Thread(() => Go(cancelSource.Token)).Start(); cancelSource.Cancel();} static void Go(CancellationToken cancelToken){ cancelToken.ThrowIfCancellationRequested(); // Imprime X hasta que se cancela for (int cycles = 0; cycles < 5; cycles++)

Console.Write('X');}

UnSafe Safe

Page 17: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Ejercicio #1 (Estados de los hilos)

Usando el formulario frmThreadingStates en el proyecto Threading2, implemente cada una de las operaciones básicas de un hilo usando cada botón de la interfaz gráfica y mostrando los valores asignando texto a la variable global “text”.

Page 18: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Nombrar hilos

public static void main(){

Thread.CurrentThread.Name = "main";Thread worker = new Thread(Go);worker.Name = "worker";worker.Start();Go();

}

static void Go(){

Console.WriteLine("Este es el hilo " + Thread.CurrentThread.Name);}

Los hilos se pueden nombrar para identificarlos en tiempo de ejecución.

De esta manera se pueden ver en el Threads Window.

Page 19: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Pasar parametros a un hilo

public static void main(){

Thread t = new Thread(() => Print("Hola mundo!"));

t.Start();} static void Print(string message){

Console.WriteLine(message);}

Usando expresiones Lambda:

public static void main(){

Thread tt = new Thread(new ParameterizedThreadStart(PrintB));

tt.Start("Hola mundo B!");}

static void PrintB(object message){

Console.WriteLine(message);}

Usando ParameterizedThreadStart:

Page 20: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Foreground y Background

Los hilos tipo foreground mantienen la aplicación viva mientras al menos uno de ellos esté corriendo.

public static void main(){

//Los hilos son foreground por defectoThread worker = new Thread(() => Console.ReadLine()); worker.Start();//El hilo principal termina pero la aplicación porque el hilo worker es foreground por

defecto}

Una vez finalizan todos los hilos foreground, todos los hilos background son matados súbitamente.

public static void main(){

Thread worker = new Thread(() => Console.ReadLine());worker.IsBackground = true;worker.Start();//El hilo principal termina y mata al hilo secundario inmediatamente por ser

background}

Page 21: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Prioridad

enum ThreadPriority { Lowest, BelowNormal, Normal, AboveNormal, Highest }

Determina cual es su tiempo de ejecución relativo a otros hilos por parte del sistema operativo relativo.

Nota: La prioridad de un hilo se debe escoger cuidadosamente para no sobrecargar la CPU.

using (Process p = Process.GetCurrentProcess()) p.PriorityClass = ProcessPriorityClass.High;

También es posible alterar la prioridad de los procesos, especialmentepara trabajo en tiempo real

worker.Priority = ThreadPriority.Highest;

Page 22: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Manejo de excepcionesLas excepciones no tienen sentido en el ámbito de la función que inicia el hilo:

public static void main(){

try{

new Thread(Go).Start();}catch (System.Exception ex){

// nunca llega aqui!

Console.WriteLine("Exception!");}

}

Las excepciones se deben controlar en la función del hilo:

static void Go(){

try{

throw null;}catch (System.Exception ex){

// Captura la excepciónConsole.WriteLine("Exception en el Go!");

}}

Page 23: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Arreglos de hilosprivate static Thread[] TArray;

public static void main(){

ThreadArray ta;//declaramos un arreglo de 5 hilosTArray = new Thread[5];for (int i = 0; i < TArray.Length; i++){

ta = new ThreadArray();TArray[i] = new Thread(new ParameterizedThreadStart(ta.Go));

}//iniciamos los 5 hilos pasando como parametro el número de cada hilo para que

imprimafor (int i = 0; i < TArray.Length; i++){

TArray[i].Start(i+1);}//Iniciamos un hilo con prioridad alta imprimiendo xta = new ThreadArray();Thread t = new Thread(() => ta.Go("x")); t.Start();

} void Go(object num){

for (int i = 0; i < 100; i++){

Console.Write(num.ToString());}

}

Page 24: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Ejercicio #2 (Otros aspectos importantes de los hilos)

Implemente una rutina con un arreglo de hilos que imprima los números del 1 al 10, 100 veces cada uno y al mismo tiempo inicie otro hilo que imprima su nombre 200 veces con mayor prioridad que los números.

Una vez impreso su nombre 200 veces, generar una excepción que en su catch aborte todos los hilos de los números.

Page 25: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Sincronización de hilos

Coordinación de las acciones de los hilos. Dividida en 4 tipos:

Bloqueo simple: Sleep, Join, Task.Wait.

Locking: Lock, Mutex, SpinLock

Nonblocking: Semaphores

Signaling:

Page 26: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Cuando dos hilos intentan acceder al mismo recurso al mismo tiempo es thread unsafe.

Thread safety

static bool done; // Los campos estáticos se comparten en todos los hilos

public static void main(){

new Thread(Go).Start();Go();

} static void Go(){

//mientras un hilo está evaluando el if el otro puede estar haciendo el writeline antes de poner done=true

if (!done) { Console.WriteLine("Done"); done = true; }}

Page 27: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Bloqueo vs Spinning

static DateTime flag;

public static void main(){

flag = DateTime.Now.AddSeconds(-40);Thread t = new Thread(Go);t.Start();t.Join();Console.Write("El hilo ha

terminado!");} static void Go(){

while (flag < DateTime.Now){

Console.Write("y");}

}

El spinning puede servir para poner a los hilos a esperar pero puede acarrear problemas de concurrencia.

Page 28: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Locking

static bool done;static readonly object locker = new object(); public static void main(){

new Thread(Go).Start();Go();

} static void Go(){

lock (locker){

if (!done) { Console.WriteLine("Done"); done = true; }}

}

Asegura que solo un hilo acceda a cierta porción de código

Cuando un hilo se enfrenta a un lock, este se pone en estado blocked (waiting) y espera hasta que se desbloquee la porción de código.

Nota: La regla de oro del locking es usarlo en cualquier operación de asignación de variables.

Page 29: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Ejercicio #3 (Locking)class ThreadUnsafe{  static int _val1 = 1, _val2 = 1;

public static void main() { Thread t = new Thread(Go); t.Start();

}

  static void Go()  {    if (_val2 != 0) Console.WriteLine (_val1 / _val2);    _val2 = 0;  }}

Considere la siguiente clase:

• Identifique el error que se produciría si otro hilo llamara a la función Go().

• Implemente otro hilo llamando a la función Go para reproducir el error.

• Resuelva el error usando los conocimientos adquiridos.

Page 30: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

SemaphoreEs como una discoteca, tiene una capacidad limitada y una vez se llena no puede entrar nadie hasta que salga otra persona.

El Lock() y el Mutex() son como semaphores con capacidad de 1.

Los semaphores son muy útiles para limitar el número de hilos que acceden a cierta función y así controlar la concurrencia.

Nota: contrario a Lock() y Mutex(), los semaphores no tienen dueño, es decir, cualquier hilo los puede liberar.

static SemaphoreSlim _sem = new SemaphoreSlim(3); // capacidad 3

public static void main(){

//5 hilos tratando de entrarfor (int i = 1; i <= 5; i++) new

Thread(Enter).Start(i);} static void Enter(object id){

Console.WriteLine(id + " quiere entrar");_sem.Wait();// sólo 3 hilos pueden estar aqui al mismo tiempoConsole.WriteLine(id + " está adentro!"); Thread.Sleep(1000 * (int)id); Console.WriteLine(id + " está saliendo"); _sem.Release();

}

Page 31: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

SignalingEl signaling se apoya en los Event Wait Handles para lograr que un hilo espere confirmación de otro hilo para realizar acciones específicas.

A continuación se muestran los diversos tipos de Event Wait Handles soportados por .NET:

Constructor Proposito Cross-process?AutoResetEvent Permite a un hilo desbloquearse apenas

reciba una señal de otro hilo.Si

ManualResetEvent Permite a un hilo desbloquearse indefinidamente cuando recibe una señal de otro hilo (hasta que ocurra un reset).

Si

ManualResetEventSlim Permite a un hilo desbloquearse indefinidamente cuando recibe una señal de otro hilo (hasta que ocurra un reset).

-

CountdownEvent Permite a un hilo desbloquearse cuando recibe un número determinado de señales.

-

Barrier Implementa una barrera de ejecución de hilo

-

Wait and Pulse Permite a un hilo desbloquearse cuando se cumpla cierta condición.

Page 32: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

AutoResetEventIgual que cuando un usuario del metro mete su ticket en el torniquete, este se desbloquea automaticamente para dejar meter otro ticket cuando el usuario pasa. Solo un ticket por usuario.static EventWaitHandle _waitHandle = new EventWaitHandle(false, EventResetMode.AutoReset);public static void main(){

new Thread(Waiter).Start();//Pausa el hilo principal por 1 segundoThread.Sleep(1000); // Envía la señal a los waiter para despertarlos_waitHandle.Set();

}

static void Waiter(){

Console.WriteLine("Esperando...");// espera por la notificación del hilo principal, que puede no llegarle porque esa señal la usa otro

Waiter

_waitHandle.WaitOne(); Console.WriteLine("Notificado!");

}

Page 33: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

ManualResetEventFunciona como una puerta ordinaria, cuando la puerta se abre todos los hilos pasan y cuando se cierra ya no pasan mas hilos.

Al usar Set() se abre la puerta, permitiendo a todos los hilos en espera continuar. Al usar Reset() se cierra la puerta.static EventWaitHandle _waitHandle = new System.Threading.ManualResetEvent(false);

public static void main(){

new Thread(Waiter).Start();new Thread(Waiter2).Start();//Pausa el hilo principal por 1 segundoThread.Sleep(1000);//Envía la señal a los dos waiter para despertarlos_waitHandle.Set();

} static void Waiter() { Console.WriteLine("Esperando..."); //esperar por la notificación del hilo principal _waitHandle.WaitOne(); Console.WriteLine("Notificado 1!"); } static void Waiter2() { Console.WriteLine("Esperando..."); //esperar por la notificación del hilo principal _waitHandle.WaitOne(); Console.WriteLine("Notificado 2!"); }

Page 34: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

CountdownEventEspera un número determinado de señales para desbloquear.

static CountdownEvent _countdown = new CountdownEvent(3);

public static void main(){

new Thread(SaySomething).Start("Hilo 1");new Thread(SaySomething).Start("Hilo 2");new Thread(SaySomething).Start("Hilo 3");//Bloquea hasta que la señal haya llegado 3 veces_countdown.Wait(); Console.WriteLine("Todos los hilos han terminado de

comunicarse!");} static void SaySomething(object thing){

Thread.Sleep(1000);Console.WriteLine(thing);_countdown.Signal();

}

Page 35: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Signaling en doble víaEs posible que la comunicación entre dos hilos se haga en doble vía usando dos Event Wait Handle, de esta manera se pueden resolver escenarios dónde un hilo debe notificar mas de una vez al otro.static EventWaitHandle _ready = new EventWaitHandle(false, EventResetMode.AutoReset);

static EventWaitHandle _go = new EventWaitHandle(false, EventResetMode.AutoReset);static readonly object _locker = new object();static string _message;public static void main(){

new Thread(Work).Start();_ready.WaitOne(); //Espera hasta que el worker esté listo lock (_locker) _message = "ooo"; _go.Set(); // Le dice al worker que siga desde el hilo principal

_ready.WaitOne();lock (_locker) _message = "ahhh"; // Envía otro mensaje al worker desde el hilo

principal _go.Set();_ready.WaitOne();

// Le dice al worker que se salga poniendo la variable _message en nulo lock (_locker) _message = null; _go.Set();}

static void Work(){

while (true){

_ready.Set(); // Indica al hilo principal que ya está listo _go.WaitOne(); // Espera señal del hilo principal para seguir

lock (_locker){

if (_message == null) return; //Se sale de este hilo Console.WriteLine(_message);

}}

}

Page 36: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

WaitAny, WaitAll, SignalAndWaitAparte de los métodos Set(), WaitOne() y Reset(), la clase WaitHandle tiene los siguiente métodos estáticos:

WaitAny: espera por cualquiera de los WaitHandle especificados.

WaitAll: espera todos los WaitHandle especificados.

SignalAndWait: llama set() en el primer WaitHandle especificado y llama WaitOne() en el segundo. Muy útil para coordinar señales entre 2 hilos.

Page 37: Juan Camilo Jiménez Dorado supercamilo@gmail.com Diciembre 2011

Ejercicio #4 (Signaling)El organismo de transito de la ciudad tiene una capacidad para 20 usuarios en su sala de espera, por lo que si llegan mas usuarios tienen que esperar afuera hasta que algun usuario sea atendido.

Al llegar a la portería del tránsito, los usuarios son atendidos por un funcionario que les recibe los papeles para iniciar su tramite y los deja pasar en caso de haber espacio en la sala de espera o cuando lo haya.

Una vez adentro de la sala de espera, la finalización del tramite dura 3 segundos antes de salir de la sala.

Es necesario conocer cuando se temrinen de atender las primeras 40 personas.

Represente la situación anterior en una rutina de .NET usando los conocimientos adquiridos en semaphores y signaling.