Upload
internet
View
106
Download
3
Embed Size (px)
Citation preview
Linguagem de Programação IV
Carlos Oberdan Rolim
Ciência da ComputaçãoSistemas de Informação
Threads
O que são Threads?
Enquanto um processo é um meio de agrupar recursos,
threads são as entidades escalonadas para a execução
sobre a CPU.
Acrescentam ao modelo de processo a possibilidade que
múltiplas execuções ocorram no mesmo ambiente de um
processo e com um grande grau de independência uma da
outra.
Processos e Threads
Recursos X escalonamento/execução
Características essenciais de um processo
são inseparáveis e tratadas em conjunto pelo S.O.
Características independentes
Podem ser tratadas independentemente pelo S.O.
Unidade de escalonamento é a thread (lightweight process)
Unidade de posse de recursos é o processo/tarefa
Threads
Threads também são chamados de lightweight process
ou processos leves.
O termo Multithread também é usado para descrever a
situação em que se permite a existência de múltiplos
threads no mesmo processo.
Multithreading
Capacidade do Sistema Operacional suportar múltiplas threads de execução dentro de um único processo
Single-Thread x Multithread
um processo
uma thread
um processo
várias threads
vários processos
uma thread por processo
vários processos
várias threads por processo
Funcionamento de uma Thread
Threads distintos em um processo não são tão
independentes quanto processos distintos, todos os threads
tem exatamente o mesmo espaço de endereçamento.
Ou seja, isso significa que eles também compartilham as
mesmas variáveis globais.
Funcionamento de uma Thread
Cada thread pode ter acesso a qualquer endereço de
memória dentro do espaço de endereçamento do processo.
Um thread pode ler, escrever ou até mesmo apagar os dados
de outra thread.
É impossível um processos distintos acessar aos dados de
outro processo.
Modelo de Processo (single thread)
Espaço de endereçamento para armazenamento da imagem do processo (espaço de usuário e de sistema)
Informações de recursos alocados são mantidos no descritor de processos
Contexto de execução (pilha, programa, dados, ...)
dados pilhaEspaço de sistema
Espaço de usuário
processo
pilha códigoSP PC
dados
Vários processos
Um fluxo de controle por processo (thread)
Troca do processo implica em atualizar estruturas de dados internas do S.O.
Contexto, espaço de endereçamento, . . .
dados pilhaEspaço
de
sistema
Espaço
de
usuário
processo
pilha códigoSP PC
dados
dados pilha
processo
pilha códigoSP PC
dados
Vários fluxos em um único processo
Um fluxo de instrução é implementado através do PC e SP
Estruturas comuns compartilhadas
Código
Dados
Descritor de processos
conceito de thread
dados pilha
processo
pilha código
SP1 PC1
dados
Espaço
de
sistema
Espaço
de
usuário
SP2
SP3
PC2
PC3
Multiprogramação Pesada
Custo do gerenciamento de processos
criação do processo
troca de contextos
esquemas de proteção, memória virtual, etc...
Custos são fator limitante na interação de processos
unidade de manipulação é o processo
Mecanismos de IPC necessitam a manipulação de estruturas complexas que representam o processo e suas propriedades (através de chamadas de sistema)
Solução
“Aliviar” os custos, reduzir o overhead envolvido
Multiprogramação Leve
Fornecido pela abstração de um fluxo de execução (thread)
Unidade de interação passa a ser função
Contexto de uma thread
Registradores(pilha, apontador de programa, registradores de uso geral)
Comunicação por memória compartilhada
Por que utilizar threads?
Permitir a exploração do paralelismo real oferecido por máquinas
multiprocessadas
Aumentar o número de atividades executadas por unidade de tempo
(throughput)
Esconder latência do tempo de resposta
possibilidade de associar threads a dispositivos de I/O
Sobrepor operações de cálculo com operações de I/O
Estados de uma Thread
Estados fundamentais:
Executando
Pronta
Bloqueada
Vantagens do Multithreading
Tempo de criação/destruição de threads é inferior que tempo de
criação/destruição de processos
Chaveamento de contexto entre threads é mais rápido que tempo de
chaveamento entre processos
Como threads compartilham o descritor do processo, elas dividem o
mesmo espaço de endereçamento o que permite a comunicação por
memória compartilhada sem interação com o núcleo (kernel) do S.O.
Segurança de Threads
Não há proteção entre as threads. Porque?
É impossível
Não é necessário (Todas as threads presumidamente são criadas pelo mesmo usuário)
Threads foram criadas para cooperar e não competir
Em que nível implementar?
Nível do usuárioGerenciamento dos Threads é feito pela aplicação.Escalonamento é feito pela aplicação
Threads x Processos
INTEL 2.2 GHz Xeon
2 CPU/node
2 GB Memory
RedHat Linux 7.3
17.4 3.9 13.5 5.9 0.8 5.3
Processo Thread
_ real user sys _ real user sys
Exemplo - RPC
Exemplo - RPC
Exemplo
t
Thread A (proc. 1)
Thread B (proc. 1)
Thread C (proc. 2)
bloqueado pronto em execução
I/O preempt
preempt
criação processo novo
Implementação de Threads
Threads são implementadas através de estrutura de dados
similares ao descritor de processo
Descritor de threads
Menos complexa (leve)
Podem ser implementadas em dois níveis diferentes
espaço de usuário (user level threads)
espaço de sistema (kernel level threads)
Single Threaded x Multithreaded
single threaded
process
control
block
user
address
space
user
stack
kernel
stack
multithreaded
user
address
space
process
control
block
user
stack
kernel
stack
thread
block
user
stack
kernel
stack
thread
block
user
stack
kernel
stack
thread
block
thread thread thread
Modelo 1:1
Threads a nível de usuário
user level threads ou process scope
Todas as tarefas de gerenciamento de threads é feito a nível da aplicação
threads são implementadas por uma biblioteca que é ligada ao programa
Interface de programação (API) para funções relacionadas com threads
Ex: criação, sincronismo, término, etc ...
O sistema operacional não “enxerga” a presença das theads
A troca de contexto entre threads é feita em modo usuário pelo escalonador embutido na biblioteca
não necessita privilégios especiais
escalonamento depende da implementação
Implementação do modelo 1:1
dados pilha
processo
pilha código
SP
dados
Espaço
de
sistema
Espaço
de
usuárioPC
PC1
PC2
PCn
SP1
SP2
SP3
biblioteca
escalonador
sistema operacional
CPU
CPU virtual
escalonador
biblioteca
Modelo 1:1
Vantagens
Sistema operacional divide o tempo do processador entre os
processos “ pesados” e, a biblioteca de threads divide o tempo do
processo entre as threads
Leve: sem interação/intervenção do S.O.
Desvantagens
Uma thread que realiza uma chamada de sistema bloqueante
bloqueia todo o processo e suas threads
Não explora o paralelismo das máquinas multiprocessadas
Modelo N:1
Threads a nível de sistema
kernel level threads ou system scope
Resolve desvantagens do modelo 1:1
O sistema operacional “enxerga” as threads
S.O. mantém informações sobre processos e sobre threads
troca de contexto necessita a intervenção do S.O.
O conceito de threads é considerado na implementação do S.O.
Implementação do modelo N:1
dados pilha
processo
pilha código
SP
dados
Espaço
de
sistema
Espaço
de
usuárioPC
PC1
PC2
PCn
SP1
SP2
SP3
escalonador
sistema operacional
CPU
CPU
virtual
CPU
virtual
CPU
virtual
Modelo N:1
Vantagens
Explora paralelismo da máquinas multiprocessadas (SMP)
Facilita o recobrimento de operações de operações de I/O por cálculos
Desvantagens
Implementação “mais pesada” que o modelo 1:1
Modelo 1:N
Permite que uma thread migre de um processo para outro.
Isto permite a movimentação da thread entre sistemas distintos
Modelo M:N
Abordagem que combina os modelos N:1 e 1:N
PTHREADS
IEEE POSIX 1003.1c standard (1995).
Implementações que aderem a este padrão são denominados como POSIX
threads ou Pthreads
Definido como um conjunto de tipos e chamadas de procedimentos da
linguagem C, implementada com um arquivo “header/include” pthread.h e uma
biblioteca de threads
Projeto de Programas com Threads
Para que um programa tenha um melhor desempenho com threads ele precisa ser organizado em tarefas distintas e independentes que podem ser executadas concorrentemente
Por exemplo, se tivermos dois ou mais procedimentos que podem ser trocados de ordem de execução, intercalados ou sobrepostos em tempo real, então eles são candidatos a serem implementados em threads distintas
Projeto de Programas com Threads
Procedimento 1 Procedimento 2 Proc Final
Procedimento 1Procedimento 2 Proc Final
P2 P2 P2 P2P1 P1 P1 P1 Proc Final
Procedimento 1
Procedimento 2 Proc Final
Tarefas candidatas a serem implementadas em threads distintas
Tarefas queTêm um potencial de ficarem bloqueadas por um longo tempo
Usam muitos ciclos de CPU
Que devem responder a eventos assíncronos
Que tenham uma importância menor ou maior do que outras tarefas
Que são aptas de serem executadas em paralelo com outras tarefas
API Pthread
Podem ser grupadas em três classes
Gerenciamento de threads
Criar, destruir, atachar, etc... threads
Mutexes
Criar, destruir, trancar e destrancar mutexes
Variáveis de condição
Funções de comunicação entre threads que têm mutexes em comum. Funções para criar, destruir, wait e signal, set e query de variáveis de condição
Convenção de nomes
Routine Prefix Functional Group
pthread_ Threads and miscellaneous subroutines
pthread_attr_ Thread attributes objects
pthread_mutex_ Mutexes
pthread_mutexattr_ Mutex attributes objects.
pthread_cond_ Condition variables
pthread_condattr_ Condition attributes objects
pthread_key_ Thread-specific data keys
Gerenciamento de threads
•Criar threads
•pthread_create (thread,attr,start_routine,arg)
•pthread_exit (status)
•pthread_attr_init (attr)
•pthread_attr_destroy (attr)
Criando e terminando threads
Criar threadspthread_create (thread,attr,start_routine,arg)
Finalizar threadspthread_exit (status)
Definir atributospthread_attr_init (attr)
Remover atributospthread_attr_destroy (attr)
Criando threads
•main() cria uma thread. As outras devem ser criadas pelo programador
•pthread_create: cria uma nova thread (pode ser chamada varias vezes)
•Argumentos da função:
•thread: identificador para nova thread que será retornado pela rotina
•attr: conjunto de atributos para definição das threads que serão criadas. NULL para valores default
•start_routine: a função que será executada assim que a thread for criada
•arg: Um argumento que será passado para a função. Ele deve ser passado por referencia com um ponteiro do tipo void (casting). NULL pode ser utilizado para não passar argumentos
•O numero maximo de threads que podem ser criadas depende da implementação
•Uma vez criadas as threads são “irmãs”. Não existe o conceito de hierarquia ou dependencia entre threads.
Terminando threads
Threads podem ser terminadas de varias formas.
A thread volta para a rotina que a criou (função main para thread inicial)
A thread faz uma chamada para a função pthread_exit()
A thread é cancelada por outra thread através da função pthread_cancel()
O processo inteiro é terminado
#include <pthread.h>#include <stdio.h>#define NUM_THREADS 5
void *PrintHello(void *threadid){
printf("\n%d: Hello World!\n", threadid);
pthread_exit(NULL);
}
int main(int argc, char *argv[]){ pthread_t threads[NUM_THREADS];
int rc, t;
for(t=0;t<NUM_THREADS;t++){
printf("Creating thread %d\n", t);
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_exit(NULL);
}
Exemplo de criação e Destruição de Thread
% gcc –o thread thread.c –lpthread% ./thread
Passagem de parâmetros
A melhor maneira de passar parametros é atraves do uso de
uma estrutura
Cada thread recebe uma instância da estrutura
#include <pthread.h>#include <stdio.h>
struct thread_data{ int thread_id; int sum; char *message;};
struct thread_data thread_data_array[NUM_THREADS];
void *PrintHello(void *threadarg){ struct thread_data *my_data;
...
my_data = (struct thread_data *) threadarg; taskid = my_data->thread_id; sum = my_data->sum; hello_msg = my_data->message;
...}
int main (int argc, char *argv[]){ ... thread_data_array[t].thread_id = t; thread_data_array[t].sum = sum; thread_data_array[t].message = messages[t];
rc = pthread_create(&threads[t], NULL, PrintHello, (void *) &thread_data_array[t]);
...
}
Exemplo de criação com passagem de parametros
para as threads usando estrutura
União e separação de threads (join and detach)
União
pthread_join (threadid,status)
Separação
pthread_detach (threadid)
Definição de atributos
pthread_attr_setdetachstate (attr,detachstate)
pthread_attr_getdetachstate (attr,detachstate)
União (join)
Mecanismo de sincronização de threadsPode ser usado mutexes e condições
pthread_join() bloqueia a função chamadora até que a thread threadid termine
O programador pode obter o status do termino de uma thread se ele for especificado em pthread_exit().
Uma união pode esperar somente um pthread_join(). É um erro lógico esperar por multiplos joins na mesma thread
pthread_detach() pode ser usado para explicitamente detach uma thread mesmo que ela tenha sido criada como joinable.
Joinable or Not?
Quando uma thread é criada um de seus atributos é a definição se uma thread é joinable ou não.
Somente threads criadas como joinable podem ser unidas (joined).
Threads criadas como detachabled nunca podem ser unidas
O padrão POSIX diz que threads tem de ser criadas como joinable
Para explicitar uma thread como joinable deve ser definido seu atributo na criação
Criar atributo do tipo pthread_attr_t pthread_attr_t attr;
Inicializar os atributos pthread_attr_init()
Definir o status de detach pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
Quando terminar, liberar recursos usados pelos atributos pthread_attr_destroy()
Joinable or joining
#include <pthread.h>#include <stdio.h>#include <stdlib.h>#include <math.h>#define NUM_THREADS 4
void *BusyWork(void *t){ int i; long tid; double result=0.0; tid = (long)t; printf("Thread %ld starting...\n",tid); for (i=0; i<1000000; i++) result = result + sin(i) * tan(i); printf("Thread %ld done. Result = %e\n",tid, result); pthread_exit((void*) t);}
int main (int argc, char *argv[]){ pthread_t thread[NUM_THREADS]; pthread_attr_t attr; int rc; long t; void *status;
/* Initialize and set thread detached attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(t=0; t<NUM_THREADS; t++) {
printf("Main: creating thread %ld\n", t);
rc = pthread_create(&thread[t], &attr, BusyWork, (void *)t);
if (rc) { printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } }
/* Free attribute and wait for the other threads */ pthread_attr_destroy(&attr); for(t=0; t<NUM_THREADS; t++) { rc = pthread_join(thread[t], &status); if (rc) { printf("ERROR; return code from pthread_join() is %d\n", rc); exit(-1); }
printf("Main: completed join with thread %ld having a status of %ld\n",t,(long)status); }
printf("Main: program completed. Exiting.\n");pthread_exit(NULL);}
Saída:
Main: creating thread 0Main: creating thread 1Thread 0 starting…Main: creating thread 2Thread 1 starting…Main: creating thread 3Thread 2 starting…Thread 3 starting…Thread 1 done. Result = -3.153838e+06Thread 0 done. Result = -3.153838e+06….Main: program completed. Exiting.
Threads e Mutexes
Mutexes são usados para controlar acesso a variaveis compartilhadas
Variáveis mutexes precisam ser declaradas pthread_mutex_t
Criar e destruir mutexes
pthread_mutex_init (mutex,attr)
pthread_mutex_destroy (mutex)
Definir e destribuir atributos de mutexes
pthread_mutexattr_init (attr)
pthread_mutexattr_destroy (attr)
Lock e unlock
pthread_mutex_lock (pthread_mutex_t *mut);
pthread_mutex_unlock (pthread_mutex_t *mut);
#include <stdio.h>#include <stdlib.h>#include <pthread.h>
void *functionC();pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;int counter = 0;
main(){ int rc1, rc2; pthread_t thread1, thread2;
/* Create independent threads each of which will execute functionC */ if( (rc1=pthread_create( &thread1, NULL, &functionC, NULL)) ){ printf("Thread creation failed: %d\n", rc1); }
if( (rc2=pthread_create( &thread2, NULL, &functionC, NULL)) ){ printf("Thread creation failed: %d\n", rc2); }
/* Wait till threads are complete before main continues. Unless we */ /* wait we run the risk of executing an exit which will terminate */ /* the process and all threads before the threads have completed. */ pthread_join( thread1, NULL); pthread_join( thread2, NULL);
exit(0);
}
void *functionC() {
pthread_mutex_lock( &mutex1 ); counter++;
printf("Counter value: %d\n",counter); pthread_mutex_unlock( &mutex1 );
}