PDF de programación - Tema 6 - Tareas y patrones de diseño para concurrencia - Programación Avanzada

Imágen de pdf Tema 6 - Tareas y patrones de diseño para concurrencia - Programación Avanzada

Tema 6 - Tareas y patrones de diseño para concurrencia - Programación Avanzadagráfica de visualizaciones

Publicado el 18 de Diciembre del 2018
239 visualizaciones desde el 18 de Diciembre del 2018
767,0 KB
48 paginas
Creado hace 7a (11/03/2013)
Asignatura 780014

Programación Avanzada

Tema 6 – Tareas y patrones de diseño para concurrencia

© luis.bengochea@uah.es

Tareas y Threads

Por ahora:

– Programa concurrente = preparación + actividades concurrentes

– Actividad concurrente = Thread nuevo


Nueva posibilidad:

– Una actividad concurrente es una tarea independiente de las otras

• Definida como una unidad de trabajo abstracta y discreta


Concepto útil en C/S:

– Cada cliente y respuesta del servidor es una tarea

– Incrementa la capacidad de prestar servicios (throughput)

– Mejora tiempos de respuesta (responsiveness) con carga normal

– Mejora el ritmo de degradación de prestaciones con carga se elevada

Estrategias de ejecución

Estrategias para ejecutar tareas:

– Secuencial

• Bajo “throughput”, tiempo de respuesta malo


– Un Thread por tarea

• Mejor uso de recursos
• Para tareas breves, la creación/destrucción de Thread es sobrecarga
• Con carga baja buena respuesta, con carga alta peligro de agotamiento


– Un grupo de Thread a reutilizar (pool)

• Se elimina sobrecarga por creación/destrucción
• Se limitan los recursos reservados
• Se alcanza estabilidad en carga alta

Las tres estrategias en esquema visual

Estrategias erróneas

Ejemplo:

– Web server que acepta conexiones por sockets en el puerto 80

– Para cada petición crea un thread nuevo

Pocos clientes



class UnreliableWebServer
{
public static void main(String[] args)
{
ServerSocket socket = new ServerSocket(80);
while (true)

{
final Socket connection = socket.accept();
Runnable r = new Runnable() { public void run() { handleRequest(connection); } };
}
//¡No hacer esto!
new Thread(r).start();
}
}



Muchos clientes

– Sólo será capaz de atender a un número reducido de clientes

Estrategia eficaz, pool de hilos

• Estructura superior al hilo que agrupa y gestiona varios hilos

– Ejecuta tareas organizadas en una cola

• Habrá más tareas que Thread

– Cada Thread puede ejecutar una tarea cada vez
– El número de Thread puede variar según necesidades
– Se pueden repartir los pools por granjas de servidores

Algoritmo de comportamiento

de cada Thread

while (true)
{
if (no tasks) wait for a task;
execute the task;
}

Pool de hilos en java

Implementado mediante el Framework Executor:

– Conjunto de interfaces

– Ejecución concurrente y asíncrona de tareas


La base del framework es la interfaz Executor

– Describe un objeto para ejecutar Runnables
public interface Executor

{

void execute(Runnable command);

}


Flexibiliza el mantenimiento

– Permite cambios de las políticas de ejecución sin cambiar el código

Políticas de ejecución en Java

En el framework Executor podemos aplicar políticas de ejecución

– Una política de ejecución incluye:

• Definir en que Thread se ejecutan las tareas

• Definir el orden en el que se ejecutan las tareas

• El número de tareas que se permiten ejecutar concurrentemente

• El número de tareas que pueden encolarse en espera de ejecución

• Selección de la tarea que debe ser rechazada si hay sobrecarga

• Definir acciones a ejecutar antes y después de una tarea



– Una política es una herramienta de gestión de recursos.

• La óptima depende de:

– Los recursos disponibles

– La calidad de servicio (throughput) que se requiere

Ciclo de vida de los Executors

Usando la interfaz ExecutorService

– Se maneja el ciclo de vida de un servicio executor:

public interface ExecutorService extends Executor
{
void shutdown();
List<Runnable> shutdownNow();
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit);

// Otros métodos convenientes para iniciar tareas

}

Interfaz ExecutorService

ExecutorService ofrece

– Servicios para controlar ciclo de vida de los Thread pool

– Un thread pool sigue un ciclo de vida con tres estados:


• Running: Cuando se crea y en régimen normal de funcionamiento.

– Admite tareas y cuando tiene threads disponibles los ejecuta.


• Shutting down: Terminando gradualmente.

– No acepta nuevas tareas, pero las que están en colase ejecutan

(aunque no hayan iniciado su ejecución)



• Terminated: Finalización abrupta.

– Trata de finalizar las tareas en ejecución, y ni admite nuevas

tareas, ni comienza la ejecución de tareas en cola.

La clase ThreadPoolExecutor

class ThreadPoolExecutor implements ExecutorService

– Tiene 4 constructores
– Los cuatro tienen en común:



int corePoolSize, // Número inicial de Thread que se crean

int maximumPoolSize, // Número máximo de threads que se pueden crear

long keepAliveTime, TimeUnit unit, // Tiempo para destruir Thread que sobran hasta corePoolSize

BlockingQueue<Runnable> workQueue, //Tipo de cola de tareas


– Además podemos tener: ningún parámetro más, uno de los dos siguientes o los dos:



ThreadFactory threadFactory, // Política con las que se crean los thread dinámicamente



// (Prioridad, Grupo, naturaleza)

RejectedExecutionHandler handler) // Define la política con la que se descartan las tareas

// tareas cuya ejecución es rechazada, bien por shutdown

// o por cola de tareas limitada

Tipos de pool de hilos

Crear pools con ThreadPoolExecutor es complejo:

– Es recomendable usar los métodos de factoría:

• Executors.newCachedThreadPool()

• Executors.newFixedThreadPool(int n)

• Executors.newSingleThreadExecutor()

• Executors.newScheduledThreadPool(int n)



– Estos métodos devuelven un ExecutorService

Executors.newCachedThreadPool()

Pool de threads no está limitado
en tamaño

– Puede reutilizar threads creados

previamente cuando se quedan
libres

– Si no hay ningún thread

disponible para una tarea nueva,
se crea uno

– Los threads que no han sido

utilizados durante un minuto, se
destruyen.

Qué pasaría si…:

1.Sistema inactivo
2.Llegan 10 tareas simultáneamente
3.Pasan 10 segundos
4.Llegan 5 tareas simultáneamente
5.Pasan 5 minutos
6.Llegan 20 tareas simultáneamente.


Tareas de entre 5 y 15
segundos para procesarse.

Executors.newFixedThreadPool(int n)

Pool que reutiliza un conjunto
fijo de threads

– Con una cola compartida

de tareas

– Si termina algún thread

debido a un fallo durante la
ejecución (antes del
shutdown), se vuelve a
crear uno nuevo para
sustituirlo.

Qué pasaría si…:

1.Sistema inactivo
2.Llegan 10 tareas simultáneamente
3.Pasan 10 segundos
4.Llegan 5 tareas simultáneamente
5.Pasan 5 minutos
6.Llegan 20 tareas simultáneamente.

Tareas de entre 5 y 15
segundos para procesarse.

Executors.newSingleThreadExecutor()

Crea un pool con un único thread

– Operando sobre una cola de

tareas

– Es el modelo seguido por el Swing

event thread

– Garantiza que las tareas se
ejecutan secuencialmente

– Garantiza que solamente hay una

activa en cualquier momento
dado

Qué pasaría si…:

1.Sistema inactivo
2.Llegan 10 tareas simultáneamente
3.Pasan 10 segundos
4.Llegan 5 tareas simultáneamente
5.Pasan 5 minutos
6.Llegan 20 tareas simultáneamente.

Tareas de entre 5 y 15
segundos para procesarse.

Executors.newScheduledThreadPool(int n)

Crea crea un pool que va a ir
ejecutando tareas programadas
cada cierto tiempo

– Puede ser en un instante dado

o de manera repetitiva

– Es parecido a un timer

• Con la diferencia de que

puede tener varios threads
para poder realizar varias
tareas programadas
simultaneamente

Qué pasaría si…:

1.Sistema inactivo
2.Llegan 10 tareas simultáneamente
3.Pasan 10 segundos
4.Llegan 5 tareas simultáneamente
5.Pasan 5 minutos
6.Llegan 20 tareas simultáneamente.

Tareas de entre 5 y 15
segundos para procesarse.

Gestión de tareas correcta

Ejemplo de un web server

– Acepta conexiones por sockets en el puerto 80

– Dispone de un pool de 7 threads para atender clientes



class ReliableWebServer
{
Executor pool = Executors.newFixedThreadPool(7);
public static void main(String[] args)
{
ServerSocket socket = new ServerSocket(80);
while (true)
{
final Socket connection = socket.accept();
Runnable r = new Runnable() { public void run() {handleRequest(connection);} };
pool.execute(r);
}
}
}

Ejemplo de finalización de las tareas del pool

Suponiendo que tuviéramos un botón en la interfaz gráfica que controla el Web Server:
private void jButtonApagar (ActionEvent evt)
{
pool.shutdown(); // No deja entrar nuevas tareas
try { // Espera durante 1 min. para ver si han terminado ya
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
{
pool.shutdownNow(); // Fuerza la terminación de las tareas
//Espera otro minuto para ver si el pool ya ha terminado
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("El pool no terminó");
}
} catch (InterruptedException ie) { // (Re-)Cancela el pool
pool.shutdownNow();
Thread.currentThread().interrupt(); //Preserva el interrupt
}
}

Tareas con resultados demorados

En el Framework Executor,

– Las tareas son objetos que implementan la interfaz Runnable,

– Su ejecución en el método public void run(){…}

– Con algunas limitaciones:
  • Links de descarga
http://lwp-l.com/pdf14577

Comentarios de: Tema 6 - Tareas y patrones de diseño para concurrencia - Programación Avanzada (0)


No hay comentarios
 

Comentar...

Nombre
Correo (no se visualiza en la web)
Valoración
Comentarios
Es necesario revisar y aceptar las políticas de privacidad