240 likes | 380 Views
Threads. Walfredo Cirne walfredo@dsc.ufcg.edu.br. 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.
E N D
Threads Walfredo Cirne walfredo@dsc.ufcg.edu.br
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 Ssenão: bloqueia esperando up(S) • up(S)se há alguém bloqueado: desbloqueie senão: incremente S
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; Exemplo de Semáforo (* exclusão mútua *) var mutex: semaphore := 1;
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/essential/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