Programação Concorrente

Propaganda
Programação Concorrente
Processos
Processo = Programa em execução.
Um processo é constituído basicamente de:






Código executável;
Pilha de execução;
Dados;
Estado;
Registradores (PC, ...);
Prioridade, arquivos abertos, ...
Cada processo tem sua própria CPU virtual.
A CPU física é compartilhada entre os vários processos:
 Multiprogramação/Compartilhamento de tempo
 Pseudoparalelismo
Um escalonador seleciona qual processo deve usar a CPU a cada momento e por quanto tempo.
Mudança de Contexto
A troca de um processo por outro na CPU chama-se mudança de contexto.
Durante uma mudança de contexto:
Estado atual do processo e seus registradores devem ser salvos;
O mapa de memória deve ser salvo;
Um novo processo deve ser escolhido;
O novo processo precisa ser iniciado;
Necessidade de troca de processos (swapping).
A mudança de contexto fornece transparência de concorrência para o usuário, contudo, é uma atividade
dispendiosa!
Implementação de Processos
O sistema operacional controla os processos a serem executados por meio do uso de uma Tabela de Processos,
a qual contém uma entrada para cada processo.
Cada entrada contém também todas as informações necessárias para a realização de mudanças de contexto:
Contador de Programa
Registradores
Ponteiro de pilha
Estado
Variáveis globais
Arquivos abertos
Processos filhos
Alarmes pendentes
Sinais e tratadores de sinais
Uso de recursos
Com isso, pode-se suspender a execução de um processo e, em um momento mais adiante, reiniciar a sua
execução a partir daquele ponto de parada.
Threads
Em uma máquina multiprocessada ou em um cluster de computadores, podemos ter vários processos (inclusive
de uma mesma aplicação) rodando ao mesmo tempo (vale salientar que em máquinas monoprocessadas o que
ocorre é um pseudoparalelismo).
Entretanto, na prática, precisamos de uma granularidade ainda menor, isto é, precisamos de paralelismo dentro
de um mesmo processo.
Esse paralelismo interno ao processo é obtido com o uso de Threads.
Alguns autores usam o termo processo leve (lightweight process) ao invés de thread. Na realidade, uma thread
é análoga a um processo tendo em vista que ambos são uma execução sequencial de um conjunto de instruções.
Contudo, uma thread é considerada leve pelo fato de rodar dentro do contexto do processo, aproveitando-se do
compartilhamento dos recursos alocados para o ambiente do programa.
Portanto, uma thread é um subconjunto das informações pertinentes a um processo e as threads de um mesmo
processo compartilham recursos entre si:
Itens por Processo
Itens por Thread
Espaço de endereçamento
Variáveis globais
Arquivos abertos
Processos filhos
Alarmes pendentes
Sinais e tratadores de sinais
Uso de recursos
Contador de Programa
Registradores
Pilha
Estado
Cada thread tem sua própria pilha de execução.
Threads são bem mais fáceis de criar e destruir, o que implica em um ganho de performance considerável.
Implementação de Threads
Há dois tipos de threads: de usuário e de sistema (núcleo).
Threads de usuário:








O sistema (núcleo) não sabe da existência de threads
Núcleo gerencia processos monothread
Cada processo tem sua própria tabela de threads
Um sistema supervisor, dentro do processo, gerencia os threads
Salva estado
Faz escalonamento
Melhor desempenho
Threads devem se comportar educadamente -> Ceder a vez quando não puderem continuar.
Threads de sistema:




O núcleo conhece as threads e as gerencia
Núcleo tem tabela de threads e tabela de processos
Bloqueio de threads = chamadas ao sistema
Núcleo escolhe outro thread do mesmo processo ou um thread de outro processo
Programação Multithreaded em Java
Grande parte das aplicações existentes usam o conceito de multitarefa (multithread). Por exemplo, quando
estamos usando um navegador WEB (Internet Explorer, Netscape, ...), ao mesmo tempo em que estamos lendo
um texto, também podemos estar efetuando o download de arquivos e ouvindo música. Para cada uma dessas
atividades, o navegador cria threads que executarão em (pseudo)paralelamente.
Nas próximas seções, estaremos vendo detalhes da implementação de aplicações multitarefa usando a
linguagem Java.
Criando uma subclasse de java.lang.Thread e rescrevendo o método run()
A linguagem Java fornece uma classe chamada Thread a partir da qual o programador pode criar suas próprias
threads. Para isso, é preciso que o programador estenda essa classe e implemente as devidas customizações,
reescrevendo o método run().
public class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i + " " + getName());
try {
sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {}
}
System.out.println("DONE! " + getName());
}
}
O método run() é onde colocamos toda a lógica de execução da thread. Em outras palavras, esse método é o
coração da thread. Particularmente no exemplo acima, o método run() da classe SimpleThread contém um laço
que é executado dez vezes. Em cada iteração, o método exibe na saída-padrão do sistema o número
correspondente àquela iteração e o nome que foi associado à thread. Então, a thread entra em modo de espera
durante um intervalo de tempo que vai de 0 a 1 segundo. Quando o laço termina, o método run imprime
DONE! seguido do nome da thread.
O programinha a seguir instancia duas threads da classe acima e inicia a execução das mesmas:
public class TwoThreadsDemo {
public static void main (String[] args) {
new SimpleThread("Jamaica").start();
new SimpleThread("Fiji").start();
}
}
Um exemplo de saída durante a execução desse programinha poderia ser:
0
0
1
1
2
2
3
3
4
4
5
5
6
6
7
7
8
9
Jamaica
Fiji
Fiji
Jamaica
Jamaica
Fiji
Fiji
Jamaica
Jamaica
Fiji
Jamaica
Fiji
Fiji
Jamaica
Jamaica
Fiji
Fiji
Fiji
8 Jamaica
DONE! Fiji
9 Jamaica
DONE! Jamaica
Implementando a interface Runnable
Uma outra técnica que pode ser utilizada para implementação de threads na linguagem Java é o uso da interface
Runnable.
Vejamos a classe a seguir:
import
import
import
import
java.awt.Graphics;
java.util.*;
java.text.DateFormat;
java.applet.Applet;
public class Clock extends Applet implements Runnable {
private Thread clockThread = null;
public void start() {
if (clockThread == null) {
clockThread = new Thread(this, "Clock");
clockThread.start();
}
}
public void run() {
Thread myThread = Thread.currentThread();
while (clockThread == myThread) {
repaint();
try {
Thread.sleep(1000);
} catch (InterruptedException e){
// the VM doesn't want us to sleep anymore,
// so get back to work
}
}
}
public void paint(Graphics g) {
// get the time and convert it to a date
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
// format it and display it
DateFormat dateFormatter = DateFormat.getTimeInstance();
g.drawString(dateFormatter.format(date), 5, 10);
}
// overrides Applet's stop method, not Thread's
public void stop() {
clockThread = null;
}
}
Como a linguagem Java não permite herança múltipla, então, nesse caso, para se poder construir uma thread é
necessário a implementação da interface Runnable.
Download