35
Programação paralela. Future e Stream Docente: MSc. Angel Alberto Vazquez Sánchez ISUTIC 2017

Programação paralela. Future e Stream · funcional es un paradigma de programación declarativa basado en el uso de funciones matemáticas, en contraste con la programación imperativa,

  • Upload
    leliem

  • View
    218

  • Download
    0

Embed Size (px)

Citation preview

Programação paralela. Future e Stream

Docente: MSc. Angel Alberto Vazquez Sánchez

ISUTIC2017

Bibliografía

● I. Foster, Designing and Building Parallel Programs: Concepts and Tools for Parallel Software Engineering, 1st ed. Addison Wesley, 1995.

● Parallel Programming in Java, Coursera Course.

Introdução

● En ciencias de la computación, la programación funcional es un paradigma de programación declarativa basado en el uso de funciones matemáticas, en contraste con la programación imperativa, que enfatiza los cambios de estado mediante la mutación de variables.

● La programación funcional tiene sus raíces en el cálculo lambda, un sistema formal desarrollado en los años 1930 para investigar la definición de función, la aplicación de las funciones y la recursión.

Introdução

● En la práctica, la diferencia entre una función matemática y la noción de una "función" utilizada en la programación imperativa, es que las funciones imperativas pueden tener efectos secundarios, como cambiar el valor de cálculos realizados previamente.

● Por esta razón carecen de transparencia referencial, es decir, la misma expresión sintáctica puede resultar en valores diferentes en varios momentos de la ejecución del programa.

● Con código funcional, en contraste, el valor generado por una función depende exclusivamente de los argumentos alimentados a la función.

● Al eliminar los efectos secundarios se puede entender y predecir el comportamiento de un programa mucho más fácilmente. Ésta es una de las principales motivaciones para utilizar la programación funcional.

Programação Funcional

A=F (B)

C=G(A)

D=H (A)

FA=Future {F (B)}

FC=Future {G(FA . get ())}

DD=Future {H (FA . get ())}

Block/Wait hasta que el objeto está disponible

F(B)

G(A) H(A)

joinjoin

Grafo de computação

Exemplo

FSUM1 = FUTURE {lowers}

FSUM2 = FUTURE {uppers}

SUM = FSUM1.get() + FSUM2.get()

Future em Java

● Em um aplicativo de thread único quando você chama um método retorna somente quando os cálculos são feitos (IOUtils.toString () vem do Apache Commons IO):

Future em Java

Além disso, para reduzir a latência, você pode querer fazer outro processamento independente, enquanto aguarda os resultados.

Nos velhos tempos, você iniciaria um novo tópico e, de alguma forma, aguardaria resultados (memória compartilhada, bloqueios, terrível espera () / notificar () par, etc.)

Future em Java

Com Future <T> é muito mais agradável:

Future em Java

É importante que você entenda os princípios. startDownloading() não bloqueia, aguardando o site externo.

Em vez disso, ele retorna imediatamente, retornando um objeto leve do Futuro <String>. Este objeto é uma promessa de que a String estará disponível no futuro.

Não sei quando, mas mantenha essa referência e, uma vez que estiver lá, você poderá recuperá-la usando Future.get ().

Em outras palavras, o futuro é um proxy ou um wrapper em torno de um objeto que ainda não existe.

Uma vez que a computação assíncrona é feita, você pode extraí-la.

Future em Java

● Future.get() é o método mais importante. Bloqueia e aguarda até que o resultado prometido esteja disponível (resolvido).

● Então, se realmente precisamos de String, basta chamar get() e aguarde.

● Existe uma versão sobrecarregada que aceita tempo limite para que você não espere para sempre se algo for selvagem.

● TimeoutException é lançado se aguardar por muito tempo.

Future em Java

● Em alguns casos de uso, você pode querer espiar no Futuro e continuar se o resultado ainda não estiver disponível. Isso é possível com isDone().

● Imagine uma situação em que seu usuário aguarda uma computação assíncrona e você gostaria que ele soubesse que ainda estamos aguardando e fazemos alguma computação enquanto isso:

Future em Java

● A última chamada para o contentsFuture.get() é garantida para retornar imediatamente e não bloquear porque Future.isDone() retornou true.

● Se você seguir o padrão acima, certifique-se de que você não está ocupado esperando, chamando isDone() milhões de tempo por segundo.

Future em Java

● Cancelar futuros é o último aspecto que ainda não cobrimos.

● Imagine que você começou algum trabalho assíncrono e você só pode aguardar por um determinado período de tempo.

● Se não estiver lá depois, digamos, 2 segundos, desistimos e propagamos um erro ou trabalhamos ao redor.

● No entanto, se você é um bom cidadão, você deve de alguma forma contar esse futuro objeto: eu não preciso mais de você, esqueça isso.

● Você economiza recursos de processamento ao não executar tarefas obsoletas.

Future em Java

● A sintaxe é simple:

contentsFuture.cancel(true);

Future em Java

● Cancelar vem em dois sabores. – Ao passar falso ao parâmetro MayInterruptIfRunning, nós apenas cancelamos tarefas que ainda não começaram, quando o Futuro representa resultados de computação que nem sequer começaram.

– Mas se nosso Callable.call() já estiver no meio, deixamos que ele termine.

– No entanto, se passarmos true, Future.cancel() será mais agressivo, tentando interromper a execução de tarefas também.

Conclusão parcial

● Então, agora entendemos o que

Future<T>

● é - um lugar-titular para algo, que você vai conseguir no futuro.

● É como uma chave para um carro que ainda não era fabricado. Mas como você realmente obtém uma instância de Future<T> en tu aplicación?

Conclusão parcial

● Duas fontes mais comuns são pools de threads e métodos assíncronos.

● Assim, nosso método startDownloading() pode ser reescrito para:

Conclusão parcial

● Muitas regras de sintaxe, mas a idéia básica é simples: envolva cálculos de longa duração em Callable<String> e submit() eles para um grupo de threads de 10 threads.

● O envio de devoluções é uma implementação do Future<String>, provavelmente de alguma forma vinculada à sua tarefa e ao pool de threads. Obviamente, sua tarefa não foi executada imediatamente.

Conclusão parcial

● Em vez disso, ele é colocado em uma fila que é mais tarde (talvez até muito mais tarde) pesquisado por thread de um pool.

● Agora, deve ficar claro o que esses dois sabores de cancel() significa - você sempre pode cancelar a tarefa que ainda reside nessa fila. Mas cancelar a tarefa já executada é um pouco mais complexo.

Conclusão parcial

● Outro lugar onde você pode conhecer Future é Spring e EJB.

● Por exemplo, no marco de trabalho Spring, você pode simplesmente anotar seu método com @Async

Marco de trabalho Fork Join

● Otra vía para utilizar Future en Java es a través del marco de trabajo Fork-Join heredando de la clase RecursiveTask.

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

Dividir e Conquistar

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

Future

public class ArraySumTask extends RecursiveTask<Double> {double[] array;int hi;int lo;public ArraySumTask(double[] array, int lo, int hi) {

this.array = array;this.hi = hi;this.lo = lo;

}@Overrideprotected Double compute() {

if(lo > hi) {return 0.0;

} else if(lo == hi) {return array[lo];

}else {int mid = (hi+lo)/2;ArraySumTask l = new ArraySumTask(array,lo, mid);ArraySumTask r = new ArraySumTask(array, mid+1, hi);r.fork();return l.compute() + r.join();

}}

}

Future GET

Marco de trabalho Fork Join

● Existem duas operações-chave que podem ser realizadas em um objeto futuro, A:– Atribuição - A pode ser atribuída uma referência a

um objeto futuro retornado por uma tarefa do formulário, future { tarefa com valor de retorno }⟨ ⟩ (usando a notação de pseudocódigo).

● O conteúdo do futuro objeto é obrigado a ser uma atribuição única (semelhante a uma variável final em Java) e não pode ser modificado após a devolução da tarefa futura.

Marco de trabalho Fork Join

● Existem duas operações-chave que podem ser realizadas em um objeto futuro, A:– Bloqueando leitura - a operação, A.get (), aguarda

até que a tarefa associada ao futuro objeto A tenha completado e, em seguida, propague o valor de retorno da tarefa como o valor retornado por A.get().

● Qualquer declaração, S, executada após A.get() pode ter certeza de que a tarefa associada ao futuro objeto A deve ter concluído antes de S iniciar a execução.

Memorização

Y 1=G (X1)

Y 2=G (X 2)

Y 3=G(X1)

Insert (G , X 1 ,Y 1)

Lookup(G , X1)Lookup(G, X 1)

FY 1=Future {G (X 1)}

Insert (G , X 1 , FY 1)

Y 2=G (X 2)

Y 3=Lookup (G , X1) .Get ()

Java Streams

for (Student student : list) {System.out.println(student);

}

list.stream().forEach(student->{System.out.println(student);

});

Forma secuencial:

Con Streams:

Java Streams

List<Student> activos = new ArrayList<>();Integer sum = 0;for (Student student : list) {

if(student.isActive)activos.add(student);

}for (Student student : activos) {

sum+=student.getAge();}Double average = sum.doubleValue()/activos.size();

Double average = list.stream().filter(s->s.isActive).mapToDouble(s->s.age).average().getAsDouble();

Forma secuencial:

Con Streams:.parallel()

Conclusões