Arquitecturas Paralelas I Computação Paralela em Larga Escala
LESI - 4º Ano
Especificação de Concorrência/Paralelismo
(gec.di.uminho.pt/lesi/ap10203/Aula05Concorrência.pdf)
João Luís Ferreira Sobral Departamento do Informática
Universidade do Minho
Outubro 2002
Arquitecturas Paralelas I 2 © João Luís Sobral 2002
Especificação de Concorrência/Paralelismo
Baseada nos conceitos de fio de execução (thread)/ processo • Fio de execução versus processo
• Paralelismo lógico versus paralelismo físico
• Preempção de fios de execução
• Escalonamento dos fios com base em prioridades
Vantagens da utilização da concorrência: • Programas que requerem a execução de várias tarefas (reactivos)
• Existem objectos dos mundo real que são activos
• Melhora a disponibilidade de serviços
• Possibilita as mensagens/invocações de métodos assíncronas
• Tira partido do paralelismo quando existem vários CPU
• Concorrência requerida (em Java várias classes executam de forma
concorrente, ex. Swing, applet, beans)
Limitações da concorrência
• Segurança (safety) – inconsistências na execução de programas
• Vivacidade (liveness) – impasses na execução de programas
• Introduz não determinismo na execução dos programas
• Em sistemas OO existem menos actividades assíncronas que objectos
• Pouco útil na execução local de métodos, num modelo chamada/resposta
• Introduz uma sobrecarga devido à criação, escalonamento e sincronização dos fios de execução
Arquitecturas Paralelas I 3 © João Luís Sobral 2002
Especificação de Concorrência/Paralelismo
Em aplicações tradicionais • Modelos de fork/join, cobegin/coend, e parfor. A sincronização é
efectua com semáforos, barreiras ou monitores.
• Processos activos (CSP), que efectuam processamento através de um corpo activo, interactuando através da passagem de mensagens. A passagem de mensagens pode ser síncrona com bloqueio, síncrona sem bloqueio ou assíncrona.
Em aplicações orientadas ao objecto • Os modelos tradicionais pressupõem um modelo de invocações
síncronas de métodos, onde o cliente fica bloqueado enquanto o método é executado pelo servidor, mesmo que não exista um valor de retorno.
• Quando o método invocado não retorna um valor pode ser efectuada
uma invocação assíncrona, podendo o cliente prosseguir a execução em simultâneo com a execução do método no servidor.
• Quando existe um valor de retorno as invocações também podem ser
assíncronas, existindo, no entanto, três alternativas para o retorno do resultado da operação:
1. Síncrona diferida - O cliente efectua uma segunda invocação ao
servidor para obter o resultado. 2. Com chamada de retorno – O servidor efectua uma invocação de
um método pré-definido do cliente, quando a execução terminou. 3. Com futuros – A invocação é delegada a outro objecto que
armazena o resultado da operação.
tempo
Cliente Servidor tarefa()
resultado()
Cliente Servidor tarefa()
resultado()
Cliente Futurotarefa()
resultado()
Servidor
tarefa()
Arquitecturas Paralelas I 4 © João Luís Sobral 2002
Programação concorrente em JAVA
JAVA é das poucas linguagens com suporte à programação concorrente
Classe java.lang.Thread: • Thread(Runnable r); // construtor da classe• start(); // cria um fio de execução e invoca r.run()• join(); // espera pelo fim da execução do fio• sleep(int ms); // suspende o fio de execução• setPriority(int Priority); // altera a prioridade
Interface Runnable
• Deve ser implementado por todas as classes pretendam ser directamente executadas por um fio de execução.
• O método run() contém o código a executar pelo fio de execução.
interface Runnable {public void run();
}
Exemplo: • Criar dois fios de execução que incrementam cada um o seu contador e
que executam em simultâneo:
public class Cont extends Thread {// implícito: implements Runnable
public Cont() { }public void run() {
for (int i=0; i<100; i++) {System.out.println(“ i= “ + i);
}}
}
• Execução sequencial: ...new Cont().run();new Cont().run();... // join
• Execução concorrente:
...new Cont().start();new Cont().start();
// ou new Cont().run();... // join, para esperar pelo fim da execução
Arquitecturas Paralelas I 5 © João Luís Sobral 2002
Programação concorrente em JAVA
Segurança – nada de mau deve acontecer num programa
Vivacidade – deve acontecer algo de bom num programa
Exemplo de falta de segurança:
• execução do método inc() por dois fios em simultâneo pode originar um valor incoerente da variável ct
public class Cont {
protected int ct;public Cont() { ct=0; }public void inc() { ct = ct + 1; }
}
Palavras reservadas para especificar sincronização
• synchronized metodo() { ... }
// metodo tem o acesso exclusivo ao objecto
• synchronized (umObj) { ... }// obtém o acesso exclusivo a umObj
Cada objecto é um monitor. Métodos da classe Object:
• wait() – espera pelo acesso ao monitor
• wait(int timeout) – wait, com temporização
• notify() – acorda um fio à espera de acesso
• notifyAll() – acorda todos os fios à espera
Exemplo de falta de vivacidade (deadlock): execução de método inc() por dois fios em simultâneo em objectos com referências cruzadas:
Obj1: .... void syncronized inc() {
obj2.inc()} .... Obj2: ... void syncronized inc() {
obj1.inc()}
Arquitecturas Paralelas I 6 © João Luís Sobral 2002
Programação concorrente em JAVA
Padrões de desenho para melhorar a segurança • Objectos imutáveis ou sem estado, uma vez que não é necessário
sincronizar o acesso estes objectos (ex. Classe String)
Ex1: class Relay {
private Server srv; // fixoRelay(Server s) { srv=s; }void do_it() { srv.do_it(); }
} Ex2:
...public int[] sort(int[] arr) {
int[] copy = arr.clone(); // cópia local// sortreturn(copy);
}
• Objectos completamente sincronizados ou com sincronização parcial. Ex1: Variáveis de classe
class Cont {
private static int contadores;void inc() {
synchronized(getClass()) {contadores++;
}}....
}
Ex2: sincronização parcial class Cell {
private int val;private Cell next;...public int synchronized getValue() {
return(val);}public void synchronized setValue(int vl) {
val=vl;}public Cell getNext() { return(next); }
}
• Objectos contidos em outros objectos (ver relay)
Arquitecturas Paralelas I 7 © João Luís Sobral 2002
Programação concorrente em JAVA
Padrões de desenho para melhorar a vivacidade • Os métodos que apenas acedem ao estado do objecto geralmente não
necessitam de ser sincronizados (excepto para double e long) • Não é necessário sincronizar as variáveis que são escritas apenas uma
vez:
Ex: ...
void setEnd() { end = True; } ...
• Utilizar sempre que possível a sincronização separada, para acesso a
cada parte do estado (ou dividir o estado em dois objectos).
Ex: class doisPontos {
Ponto p1, p2;public void movexp1(int x) {
synchronized (p1) {p1.movex(x);
}}public void movexp2(int x) {
synchronized (p2) {p2.movex(x);
}}
}
• Estruturas de dados ligadas podem utilizar fechos separados para a inserção e para a remoção.
• Os recursos devem ser acedidos sempre pela mesma ordem para
minimizar os impasses.
Ex:
public void update() {synchronized(obj1) {
synchronized(obj2) {... // do update
}}
}
Arquitecturas Paralelas I 8 © João Luís Sobral 2002
Acções Dependentes do Estado dos Objectos
Que acção tomar quando um objecto não pode satisfazer um pedido?
• Ignorar o pedido • Retornar uma indicação de falha (excepção?) • Suspender o cliente até que a condição se verifique • Tomar uma acção provisória • Prosseguir por forma a poder repor o estado se algo correr mal • Repetir a acção até ser possível executá-la
Estratégias pessimistas e estratégias optimistas
Representação lógica do estado versus representação física do estado
• Estado lógico: normalmente definido por predicados: expressões Booleanas em função do estado (ex. buffer cheio, meio e vazio)
• O estado lógico pode ser representado explicitamente em variáveis
Suspensão do cliente através de guardas
• Métodos wait(), notify() e notifyAll() da classe Object
• Exemplo1 (wait()) public class GuardedClass {
protected boolean cond=false;protected synchronized void awaitC() {
while(!cond) {try { wait(); }catch (InterruptedException e) {}
}}public synchronized void guardedAction() {
awaitC();// ...
}public synchronized void setCond() {
cond=true;notifyAll();
}}
• Exemplo2 (espera activa – pouco eficiente)
protected void spinWaitC() {while(!cond) { Thread.yield(); }
}
• Problemas com monitores encadeados: wait() apenas liberta o fecho
desse monitor
Arquitecturas Paralelas I 9 © João Luís Sobral 2002
Implementação de Invocações Assíncronas de Métodos
Invocação assíncrona sem valor de retorno
• Implementada através de um padrão comando, onde o comando é executado em paralelo com o cliente. Os parâmetros do comando são passados no seu construtor.
• A activação do comando pode ser efectuada pelo cliente ao pelo próprio
comando.
• Activação pelo cliente:
public class FileWriter extends Thread {private String nm;private byte[] d;
public FileWriter(String n, byte data[]) {nm = n;d = data;
}public void run() {
writeBytes(nm,d);}
}
// código do cliente(new FileWriter(“Pic”,rawPicture)).start();
• Activação pelo servidor
public class FileWriter extends Thread {
private String nm;private byte[] d;
public FileWriter(String n, byte data[]) {nm = n;d = data;start();
}public void run() {
writeBytes(nm,d);}
}
// código do clientenew FileWriter(“Picture”,rawPicture);
Arquitecturas Paralelas I 10 © João Luís Sobral 2002
Implementação de Invocações Assíncronas de Métodos
Invocação assíncrona com valor de retorno: como esperar pelo resultado operação realizada por um fio de execução?
Síncrona diferida - Método Thread.join()
r = new Service().start();// .. doWork();r.join();r.getResult();
Futuros
• É utilizado um objecto que irá conter o resultado da operação e que bloqueia o
cliente caso seja requerido o valor antes de estar disponível. Este objecto deve ser partilhado pelo comando e pelo cliente. É implementado através de um Join.
class Future extends Thread {
private Task tk=null;
public Future(Task tsk) {tk = tsk;start();
}public Task getResult() {
join();return(tk);
}public void run() {
tk = do_task(); // realiza a tarefa}
}
Chamadas de retorno
public interface Client {public void opOK(Task);
}
class OPCallBack extends Thread {private Client cl=null;private Task tk=null;
public OPCallBack(Task tsk, Client clk) {tk = tsk;cl = clkstart();
}public run() { cl.opOK(do_task()); }
}
Arquitecturas Paralelas I 11 © João Luís Sobral 2002
Implementação de Invocações Assíncronas de Métodos
.Net - Invocação assíncrona sem valor de retorno
• A invocação assíncrona de métodos é directamente suportada em .Net remoting.
• A invocação de métodos sem valor de retorno (One Way) é efectuada marcando o método OneWay e utilizando um delegate para efectuar a invocação.
public class BaseObject {
[OneWay]public void writeBytes(string, int);...
}
// delegate para uma função void xxx(string,int)delegate void writeBytesDelegate(string, int);
static void Main(string[] args) {
ob = new BaseObject;
// cria um delegate para ab.writeByteswriteBytesDelegate wbd =
new writeBytesDelegate(ob.writeBytes);
// inicia a invocaçãoIAsyncResul as =
wbd.BeginInvoke(“Pic”,rawPic,null,null);
// EndInvoke() retorna imediatamentewbd.EndInvoke(as);
}
.Net - Síncrona diferida / Futuros
delegate int serviceDelegate();
public static int service() {// ...
}
static void Main(string[] args) {serviceDelegate dl =
new serviceDelegate(service);IAsyncResult ar = dl.BeginInvoke(null,null);// ... doWork();int res = dl.EndInvoke();
}
Arquitecturas Paralelas I 12 © João Luís Sobral 2002
Programação Concorrente/Paralela
Exercícios
1 Codificar e executar o programa exemplo dos dois contadores em paralelo. 2 Desenvolver um programa que implemente um relógio, com uma precisão
de segundos e que execute em simultâneo com o resto do programa. Utilize o seguinte método:
void sleep(miliseconds) throws InterruptedException
3 Implemente um contador com operações de incremento e decremento, que nunca fique inferior a zero, nem superior a 100. Implemente três versões distintas: com indicação de falha, métodos guardados com base no estado físico e métodos guardados com base no estado lógico. Escreva também um programa com dois fios de execução um que incrementa o contador e outro que decrementa o contador, para efectuar testes a esta classe.
Nota: Na versão com indicação da falha, crie uma nova excepção:
public class CannotIncException extends Exception {} que pode ser gerada através de:
throw new CannotIncException();
4 Desenvolva um programa que execute os contadores da alínea 1) em objectos servidores remotos, executando em paralelo.
5 Altere o programa anterior por forma a que o objecto que cria os contadores
seja notificado do fim da contagem (i.e. com Callback).
6 Repita o exercício 4, mas agora em .Net, utilizando um delegate para implementar as invocações assíncronas de métodos.