Threads
Walfredo Cirne
[email protected]
Memória Compartilhada
Uma questão básica em computação
paralela é como as tarefas se comunicam
Uma alternativa é que as várias tarefas
compartilhem a mesma memória
Neste caso, as tarefas podem ser
implementadas como processos ou threads
Processos  Threads
Processos executam em espaço de
endereçamento próprio, de modo a evitar
interferência de outros processos
Threads de um processo usam o mesmo
espaço de endereçamento
Threads são mais “leves” que processos

Além do que, pode-se criar threads a nível de
usuário (green threads)
Processos
[possível criação de seg. memória compartilhada]
ppid = fork();
if (ppid < 0) {
fork_error_function();
} else if (ppid == 0) {
child_function();
} else {
parent_function();
}
Threads
Threads de um processo usam o mesmo
espaço de endereçamento
O programador e a linguagem devem
prover proteção contra interferências
Algumas linguagens possuem modelo de
concorrência próprio (ex: ADA e Java)
Outras linguagens utilizam extensões para
lidar com threads (ex: C tipicamente usa
Posix)
POSIX
POSIX: portable operating system
interface
Objetivo: Portabilidade do código fonte
quando serviços do sistema operacional
se fazem necessários
Padrão IEEE
Aplicabilidade de Threads
Threads são muito úteis para paralelismo
de “pequena escala”



I/O não blocado
Interface com o usuário
Construção de servidores
Threads também são úteis em sistemas
de tempo real, onde a concorrência é
intrinseca ao problema
Modelo de Programação
Um modelo de programação paralelo
deve implementar três primitivas básicas



Criação de processos/threads
Sincronização de processos
Comunicação entre processos
 Em memória compartilhada, a comunicação é
feita pela própria memória
Necessidade de Sincronização
Por exemplo, considere dois processos
atualizando uma variável comum:

x=x+1
Operações:



Carregar x em um registro
Incrementar registro
Armazenar registro em x
C + Posix: Criação de Threads
Criação de thread via:
int pthread_create ( pthread_t *thread_id,
const pthread_attr_t *attributes,
void *(*thread_function)(void *),
void *arguments );
Termino quando função termina ou via:
int pthread_exit (void *status);
Um thread pode esperar por outro via:
int pthread_join (pthread_t thread, void **status_ptr);
Semáforo
Semáforo S: variável não-negativa inteira
que só pode se modificada pelos
procedimentos up() e down()
down(S)
se S > 0: decremente S
senão: bloqueia esperando up(S)
up(S)
se há alguém bloqueado: desbloqueie
senão: incremente S
Exemplo de Semáforo
(* exclusão mútua *)
var mutex: semaphore := 1;
thread P1;
statement X
down(mutex);
statement Y
up(mutex);
statement Z
end P1;
thread P2;
statement A;
down(mutex);
statement B;
up(mutex);
statement C;
end P2;
Mutex Posix
Mutex é um semáforo binário


down() é chamado de lock()
up() é chamado de unlock()
Posix tem as seguintes operações:
int pthread_mutex_init (pthread_mutex_t *mut,
const pthread_mutexattr_t *attr);
int pthread_mutex_lock (pthread_mutex_t *mut);
int pthread_mutex_unlock (pthread_mutex_t *mut);
int pthread_mutex_trylock (pthread_mutex_t *mut);
int pthread_mutex_destroy (pthread_mutex_t *mut);
Condições
Mutex servem para proteção de regiões
críticas
Além disso, precisamos esperar que outro
thread faça alguma coisa
Resolvido em Posix através de condições
Condições Posix
int pthread_cond_init (pthread_cond_t *cond,
pthread_condattr_t *attr);
int pthread_cond_wait (pthread_cond_t *cond,
pthread_mutex_t *mut);
int pthread_cond_signal (pthread_cond_t *cond);
int pthread_cond_broadcast (pthread_cond_t *cond);
int pthread_cond_timedwait (pthread_cond_t *cond,
pthread_mutex_t *mut,
const struct timespec *abstime);
int pthread_cond_destroy (pthread_cond_t *cond);
Exemplo Posix
Produtor/Consumidor retirado de
www.uq.edu.au/~cmamuys/humbug/talks/
pthreads/pthreads.html
Monitores
Lidar com mutexes e condições é muito
baixo nível e muito sujeito a erros
Monitores encapsulam seções críticas
como procedimentos de um modulo, sendo
portanto muito mais fáceis de programar


Todas as chamadas ao procedimentos são
executadas sob exclusão mútua
Estruturas de sincronização estão embutidas
no monitor
Threads Java
Java tem monitores “orientados a objeto”
Em Java, existe um lock associado a cada
objeto que não pode ser acessado
diretamente pela aplicação
Quando um método é identificado como
synchronized, o acesso e feito apenas
quando o lock associado com o objeto é
obtido
Criando Threads em Java [1]
public class SimpleThread extends Thread {
public SimpleThread(String str) {
super(str);
}
public void run() {
[ código do thread ]
}
}
Criando Threads em Java [2]
public class C extends D implements Runnable {
private Thread thread = null;
public void start() {
if (clockThread == null) {
thread = new Thread(this, “CThread");
thread.start();
}
}
public void run() {
[código do thread ]
}
}
Exclusão Mútua em Java
class SharedInteger {
private int theData;
public SharedInteger(int initialValue) {
theData = initialValue;
}
public synchronized int read() {
return theData;
}
public synchronized void write(int newValue) {
theData = newValue;
}
}
Sincronização em Java
Java associa condições a cada objeto
wait() espera pela condição do objeto
notify() e notifyAll() acordam threads
esperando pela condição
Exemplo Java
Produtor/Consumidor retirado de
java.sun.com/docs/books/tutorial/essen
tial/threads




ProducerConsumerTest.java
CubbyHole.java
Producer.java
Consumer.java
Agradecimentos
A Fabricio Silva que gentilmente nos
forneceu seu material sobre tempo real,
usados como base destas transparências
Download

Introducao aos sistemas em Tempo Real