jueves, 26 de junio de 2014

Receta Multithreading No. 1-3 en C#: Poner en Espera un Thread

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
3.1 Firma Join
3.2 Firma Join(Int32)
3.3 Firma Join(TimeSpan)
4. Práctica: Código C#
5. Conclusiones
6. Glosario
7. Literatura & Enlaces

0. Introducción

En la receta multithreading previa (Pausar un Thread) conocimos el método Sleep para pausar un thread durante una cantidad de tiempo (en milisegundos) determinada; sin embargo para los casos que se desconoce el tiempo de espera (debido a la disponibilidad de recursos de máquina, por ejemplo) se requiere el uso de una alternativa más apropiada para los tiempos de espera variables. Veremos que la clase Thread provee el método Join para satisfacer esta necesidad, de distintas maneras.

1. Problema

Se requiere crear un programa capaz de realizar cálculos de manera sincrónica, es decir, hasta no finalizar una tarea, el resto de tareas pendientes por ejecutar quedarán en espera.

2. Solución

La clase Thread (System.Threading) está compuesta por el método Join. Este método permite poner en espera el thread que realiza la invocación durante el tiempo que tarde en ejecutarse la instancia (tipo Thread) sobre la que se invoca. Este método provee la siguiente lista de métodos sobrecargados:
  • Join
  • Join(Int32), y 
  • Join(TimeSpan)
Exploraremos cada una de estas firmas en la sección 3.

3. Discusión de la Solución


A continuación describiré las tres firmas sobrecargadas del método Join pertenecientes a la clase Thread.
Versiones sobrecargadas del método Thread.Join
Figura 1. Versiones sobrecargadas del método Thread.Join.

3.1 Firma Join

La versión sobrecargada más sencilla de Join posee la siguiente firma [3]:

public void Join()

La tarea de Join es bloquear el método desde donde se invoca hasta que la instancia de Thread haya terminado de ejecutar el método encapsulado por el delegado ThreadStart [4]. Esta forma de funcionamiento permite implementar un mecanismo de sincronización para el acceso a recursos compartidos (e.g., archivo, área de memoria).

Ejemplo de uso:

En la línea 9 definimos tres variables de tipo Thread. En la línea 15 creamos la instancia Thread thread1 y encapsulamos implícitamente el método ProcesoThread en una instancia del delegado ThreadStart. De forma análoga procedemos para la variable thread2. En el método ProcesoThread, sobre la línea 31 mostramos el nombre del thread que entra en ejecución. Con la sentencia if (líneas 33 y 34) validamos que el nombre del thread actual corresponda con el nombre Thread1 y además que la instancia thread2 esté en ejecución (thread2.ThreadState != ThreadState.Unstarted).

Compilación:


  1. csc /target:exe UsoThreadJoin.cs

Ejecución assembly:


  1. .\EnsambladorRegex.exe

Prueba de ejecución (ideone).

Prueba de ejecución (local):
Uso del método Join.
Figura 2. Uso del método Join.

3.2 Firma Join(Int32)

A diferencia de la firma Join(), con Join(Int32) [5] además de bloquear el thread que invoca a este método, podemos especificar un límite de tiempo en el que puede estar en espera el método que le invoca. Aquí está su firma:

public bool Join(int millisecondsTimeout)

Descripción puntual:
  • Parámetros:
    • int millisecondsTimeout: número de milisegundos de espera.
  • Valor de retorno:
    • bool: true si el thread terminó de ejecutar el método encapsulado por el delegado ThreadStart. false si el thread no ha terminado de ejecutarse en el tiempo especificado por el parámetro millisecondsTimeout.
Esta forma de funcionamiento permite implementar un mecanismo de sincronización para el acceso a recursos compartidos (e.g., archivo, área de memoria).

Ejemplo de uso:

En la línea 9 definimos tres variables de tipo Thread. En la línea 15 creamos la instancia Thread thread1 y encapsulamos implícitamente el método ProcesoThread en una instancia del delegado ThreadStart. De forma análoga procedemos para la variable thread2. En el método ProcesoThread, sobre la línea 31 mostramos el nombre del thread que entra en ejecución. Con la sentencia if (líneas 33 y 34) validamos que el nombre del thread actual corresponda con el nombre Thread1 y además que la instancia thread2 esté en ejecución (thread2.ThreadState != ThreadState.Unstarted).


Continuando, con la sentencia if (línea 37) invocamos a Join especificando 2 segundos de espera mientras que el thread thread2 termina su ejecución. En caso de que termine antes de los 2 segundos, se mostrará el mensaje de la línea 39, de lo contrario se mostrara el mensaje de la línea 43.


Compilación:


  1. csc /target:exe UsoThreadJoinInt32.cs

Ejecución assembly:


  1. .\UsoThreadJoinInt32.exe

Prueba de ejecución (ideone).

Prueba de ejecución (local):
Uso del método Join(Int32).
Figura 3. Uso del método Join(Int32).

3.3 Firma Join(TimeSpan)

Con Join(TimeSpan) podemos especificar el tiempo de espera de ejecución del thread sobre el que se invoca este método como una instancia de la estructura TimeSpan [6]. Firma:

public bool Join(TimeSpan timeout)

Descripción puntual:
  • Parámetros:
    • TimeSpan timeout: cantidad de tiempo de espera para el thread sobre el que se invoca el método Join termina.
  • Valor de retorno:
    • true si el thread terminó de ejecutar el método encapsulado por el delegado ThreadStartfalse si el thread no ha terminado de ejecutarse en el tiempo especificado por el parámetro timeout.
Ejemplo de uso:

Archivo C# UsoThreadJoinTimeSpan.cs {enlace alternativo}:

En la línea 10 creamos una instancia de la estructura TimeSpan para representar un segundo. Creamos un instancia de Thread en la línea 16, e invocamos el método Start (línea 17) para iniciar inmediatamente el thread. Con la sentencia if (línea 20) validamos que la invocación de Join sobre nuevoThread tarde máximo 2 segundos (tiempoEspera + tiempoEspera).

Compilación:


  1. csc /target:exe UsoThreadJoinTimeSpan.cs

Ejecución assembly:


  1. .\UsoThreadJoinTimeSpan.exe

Prueba de ejecución (ideone).

Prueba de ejecución (local):
Uso del método Join(TimeSpan).
Figura 4. Uso del método Join(TimeSpan).

4. Práctica: Código C#

Afiancemos el conocimiento que adquirimos en la sección anterior con un ejemplo en el que esperamos la impresión de una serie de números (simula un calculo largo) mientras el thread Main espera.

Compilación:


  1. csc /target:exe ImpresorNumeros.cs

Ejecución assembly:


  1. .\ImpresorNumeros.exe

Prueba de ejecución (ideone).

Prueba de ejecución (local):
Prueba ejecución assembly ImpresorNumeros.exe
Figura 5. Prueba ejecución assembly ImpresorNumeros.exe.

4. Conclusiones

Demostré el proceso de colocación en espera de un thread a través del método Join, Join(Int32), o Join(TimeSpan) de la clase System.Threading.Thread. Este enfoque nos permite aplicar el mecanismo de sincronización de acceso a recursos compartidos (e.g., área de memoria, escritura sobra un archivo en disco). Para la próxima receta multithreading conoceremos el proceso necesario para abortar o cancelar un thread.

Glosario

  • Intervalo de espera
  • Multithreading
  • Recurso compartido
  • Sincronización
  • Thread

Literatura & Enlaces

[1]: Multithreading in C# 5.0 Cookbook by Eugene Agafonov. Copyright 2013 Eugene Agafonov, 978-1-84969-764-4.
[2]: Receta Multithreading No. 1-2 en C#: Pausar un Thread | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/06/receta-multithreading-no-1-2-en-csharp-pausar-un-thread.html
[3]: Thread.Join Method (System.Threading) - http://msdn.microsoft.com/en-us/library/95hbf2ta(v=vs.110).aspx
[4]: ThreadStart Delegate (System.Threading) - http://msdn.microsoft.com/en-us/library/system.threading.threadstart(v=vs.110).aspx
[5]: Thread.Join Method (Int32) (System.Threading) - http://msdn.microsoft.com/en-us/library/6b1kkss0(v=vs.110).aspx
[6]: TimeSpan Structure (System) - http://msdn.microsoft.com/en-us/library/System.TimeSpan(v=vs.110).aspx
[7]: Thread.Join Method (TimeSpan) (System.Threading) - http://msdn.microsoft.com/en-us/library/23f7b1ct(v=vs.110).aspx


S

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.