Esta é uma pré-visualização de arquivo. Entre para ver o arquivo original
Italo Valcy Programação em C, 2013.1
Programação C
Italo Valcy <italo@dcc.ufba.br>
Aula 09 – Programação concorrente (threads)
Italo Valcy Programação em C, 2013.1 2 / 34
Todo o material aqui disponível pode, posteriormente, ser utilizado sobre os termos da:
Creative Commons License:
Atribuição - Uso não comercial - Permanência da Licença
http://creativecommons.org/licenses/by-nc-sa/3.0/
Licença de uso e distribuição
Italo Valcy Programação em C, 2013.1 3 / 34
Motivação
Desempenho
Conseguir reduzir o tempo de execução dos
programas em máquinas multiprocessadas
Modelagem
Descrever o comportamento natural de algumas
aplicações (operações I/O, ocorrência assíncrona
de eventos, tarefas independentes, etc.)
Italo Valcy Programação em C, 2013.1 4 / 34
Programação concorrente e paralela
Na programação sequencial todas as
instruções de um programa são executadas
através de uma única linha de execução
Na programação concorrente e paralela um
programa pode ser executado através de
diversas linhas de execução
Italo Valcy Programação em C, 2013.1 5 / 34
Programação concorrente e paralela
Italo Valcy Programação em C, 2013.1 6 / 34
Paralelismo físico e lógico
Italo Valcy Programação em C, 2013.1 7 / 34
Concorrência com Processos
Um processo é um fluxo de controle sequencial e
seu espaço de endereçamento (variáveis,
registradores, arquivos, etc.)
Cada processo possui um contexto, que deve ser
salvo quando o processo é interrompido e relido
quando ele for retomado
Um processo é uma abstração do S.O.
Um processo pode criar outros processos para
realizar atividades independentes:
fork()
Italo Valcy Programação em C, 2013.1 8 / 34
Concorrência com Processos
Italo Valcy Programação em C, 2013.1 9 / 34
Concorrência com Threads
Linhas de execução concorrentes
Permitem múltiplas atividades independentes
dentro de um único processo
Threads de um mesmo processo compartilham:
Todo o espaço de endereçamento, exceto a pilha,
os registradores e o contador de programa
Arquivos abertos
Outros recursos
Italo Valcy Programação em C, 2013.1 10 / 34
Concorrência com Threads
Italo Valcy Programação em C, 2013.1 11 / 34
Processos vs Threads
Fonte: https://computing.llnl.gov/tutorials/pthreads/
Italo Valcy Programação em C, 2013.1 12 / 34
Processos
Para criar processos no Linux, deve-se usar a
chamada de sistema fork()
fork() faz com que o processo atual seja
dividido em dois novos processos – processo
pai e processo filho
Todas as páginas de memória do processo
original são duplicadas em uma chamada ao
fork(), assim ambos, pai e filho, tem acesso a
todas as informações.
fork()
Italo Valcy Programação em C, 2013.1 13 / 34
Processos
Valor de retorno:
No pai: Processo ID (PID) do filho
No filho: 0 (zero)
Se o fork() falhar por algum motivo (falta de
memória, muitos processos, etc.), ele não cria
um novo processo, e retorna -1.
fork()
Italo Valcy Programação em C, 2013.1 14 / 34
Processos
fork() – exemplo
#include<stdio.h>
#include<unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid;
pid = fork();
if (pid<0) {
printf("Erro ao criar novo processo\n");
return 1;
} else if (pid == 0) {
printf("Filho... PID=%d\n", getpid());
} else {
printf("Pai... PID=%d\n", getpid());
wait(NULL);
}
printf("%d encerrando...\n", getpid());
return 0;
}
Italo Valcy Programação em C, 2013.1 15 / 34
Processos
fork()
int main() {
int pid, x = 4;
pid = fork();
if ( pid == 0 ) {
fork();
x=x+2;
} else {
x;
}
printf(“x=%d\n”,x);
}
Exercício: qual a saída do programa abaixo?
Italo Valcy Programação em C, 2013.1 16 / 34
POSIX Threads
Norma internacional IEEE POSIX 1003.1 C
Threads POSIX podem implementar threads em
nível de usuário, em nível de kernel ou misto
O programa em C deve conter:
#include <pthread.h>
E para compilar, é necessário incluir a opção
-pthread
gcc pthread programa.c
Italo Valcy Programação em C, 2013.1 17 / 34
POSIX Threads
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5
void *PrintHello(void *arg) {
int *tid = (int *)arg;
printf("Hello World! It's me, thread #%d!\n", *tid);
pthread_exit(NULL);
}
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int thread_id[NUM_THREADS];
int rc, t;
for(t=0; t<NUM_THREADS; t++){
printf("In main: creating thread %d\n", t);
thread_id[t] = t;
rc = pthread_create(&threads[t], NULL, PrintHello, (void *)&thread_id[t]);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(1);
}
}
/* Last thing that main() should do */
pthread_exit(NULL);
}
Exemplo
Italo Valcy Programação em C, 2013.1 18 / 34
POSIX Threads
~$ gcc pthread o pthreadhello pthreadhello.c
~$ ./pthreadhello
In main: creating thread 0
In main: creating thread 1
Hello World! It's me, thread #0!
Hello World! It's me, thread #1!
In main: creating thread 2
In main: creating thread 3
Hello World! It's me, thread #2!
Hello World! It's me, thread #3!
In main: creating thread 4
Hello World! It's me, thread #4!
Exemplo – possível saída
Italo Valcy Programação em C, 2013.1 19 / 34
Estruturas e funções usadas
pthread_t
pthread_create()
pthread_join()
pthread_kill
pthread_exit()
Italo Valcy Programação em C, 2013.1 20 / 34
Estruturas e funções usadas
Estrutura de dados para armazenar
informações sobre as threads. Para uso interno
do pthread.h
Usada na criação, no join, na finalização, etc
Exemplo:
pthread_t
#include <pthread.h>
#define NUM_THREADS 5
pthread_t threads[NUM_THREADS];
...
Italo Valcy Programação em C, 2013.1 21 / 34
Estruturas e funções usadas
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Criação de novas threads:
Local para salvar as informações da thread (vetor de pthread_t)
Atributos da thread (stack size, sched method, etc.)
Função da thread
Argumentos (apontador)
Retorna 0 (zero) se a criação tiver sucesso.
pthread_create()
void *myfunc(void *arg) {
...
return (void *)result;
}
int main() {
pthread_t onethread;
int s = pthread_create(&onethread, NULL, &myfunc, NULL);
}
Italo Valcy Programação em C, 2013.1 22 / 34
Estruturas e funções usadas
pthread_create() – Argumentos
pthread_t threads[2];
void *thread_func(void *arg) {
int *n = (int *)arg;
...
}
int main(int argc, char **argv) {
int i, a = 10;
for(i=0; i<2; i++) {
pthread_create(&threads[i], NULL, &thread_func, &a);
}
...
}
Italo Valcy Programação em C, 2013.1 23 / 34
Estruturas e funções usadas
int pthread_join(pthread_t thread, void *retval);
Aguarda a finalização de uma thread (diretiva de
sincronização)
É possível passar um apontador para obter retorno da
thread
Em sucesso, retorna 0;
pthread_join()
...
s = pthread_join(onethread, &res);
...
Italo Valcy Programação em C, 2013.1 24 / 34
Estruturas e funções usadas
void pthread_exit(void *retval);
Finaliza uma thread e retorna algum valor
através de retval (pode-se obtê-lo através de
pthread_join)
pthread_exit()
...
pthread_exit(&result);
...
Italo Valcy Programação em C, 2013.1 25 / 34
Exercício
Fazer um programa que recebe diversos
argumentos de linha de comando e cria uma
thread para imprimir cada argumento na saída
padrão.
Italo Valcy Programação em C, 2013.1 26 / 34
Exercício
#include <stdio.h>
#include <stdlib.h>
#include
<pthread.h>
typedef struct mythread_args {
int id;
char *arg_string;
} mythread_args_t;
void *mythread_handler(void *arg) {
mythread_args_t *myarg = (mythread_args_t *)arg;
printf("Thread #%d arg: %s\n", myarg>id, myarg>arg_string);
}
pthread-print-args.c (1/2)
Italo Valcy Programação em C, 2013.1 27 / 34
Exercício
int main(int argc, char **argv) {
pthread_t *threads;
mythread_args_t *thread_args;
int i, s;
if (argc <= 1) {
printf("%s <ARG1> [<ARG2> ...]\n", argv[0]);
}
argc;
threads = (pthread_t *)malloc(sizeof(pthread_t)*argc);
thread_args = (mythread_args_t *)malloc(sizeof(mythread_args_t)*argc);
for(i=0; i < argc; i++) {
thread_args[i].id = i;
thread_args[i].arg_string = argv[i+1];
printf("MAIN Criando thread #%d\n", i);
s = pthread_create(&threads[i], NULL, &mythread_handler, &thread_args[i]);
if (s!=0) {
printf(" > Erro ao criar thread!\n");
exit(1);
}
}
for(i=0; i < argc; i++) {
s = pthread_join(threads[i], NULL);
if (s!=0) {
printf("Error na thread %d!\n", i);
exit(1);
}
}
return 0;
}
pthread-print-args.c (2/2)
Italo Valcy Programação em C, 2013.1 28 / 34
POSIX Threads
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5
int saldo = 1000;
void *atualizaSaldo(void *arg) {
int *tid = (int *)arg;
int meu_saldo = saldo;
int novo_saldo = meu_saldo + (*tid + 1)*100;
printf("Thread #%d Novo saldo: %d\n", *tid, novo_saldo);
saldo = novo_saldo;
pthread_exit(NULL);
}
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int thread_id[NUM_THREADS];
int rc, i;
for(i=0; i<NUM_THREADS; i++){
thread_id[i] = i;
rc = pthread_create(&threads[i], NULL, &atualizaSaldo, (void *)&thread_id[i]);
if (rc) exit(1);
}
for(i=0; i < NUM_THREADS; i++)
pthread_join(threads[i], NULL);
printf("Saldo final: %d\n", saldo);
return 0;
}
Exemplo 2
Italo Valcy Programação em C, 2013.1 29 / 34
POSIX Threads
Exemplo 2 – ordem de execução
A ordem de execução não é garantida!
Necessário: uso de mecanismos de
sincronização!
Italo Valcy Programação em C, 2013.1 30 / 34
Mecanismos de sincronização
Exclusão mútua
Uma thread está executando sozinha um
determinado código, enquanto as outras esperam
para poder executar
Sessão crítica
Parte do programa que deve ser executada por
somente uma thread de cada vez (em exclusão
mútua)
Italo Valcy Programação em C, 2013.1 31 / 34
Mecanismos de sincronização
Semáforos / Mutex
Monitores
Conditions
Trocas de mensagem
Italo Valcy Programação em C, 2013.1 32 / 34
Mutex
Exemplo
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 5
int saldo = 1000;
pthread_mutex_t saldo_mutex;
void *atualizaSaldo(void *arg) {
int *tid = (int *)arg;
pthread_mutex_lock(&saldo_mutex);
int meu_saldo = saldo;
int novo_saldo = meu_saldo + (*tid + 1)*100;
printf("Thread #%d Novo saldo: %d\n", *tid, novo_saldo);
saldo = novo_saldo;
pthread_mutex_unlock(&saldo_mutex);
pthread_exit(NULL);
}
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int thread_id[NUM_THREADS];
int rc, i;
pthread_mutex_init(&saldo_mutex, NULL);
for(i=0; i<NUM_THREADS; i++){
thread_id[i] = i;
rc = pthread_create(&threads[i], NULL, &atualizaSaldo, (void *)&thread_id[i]);
if (rc) exit(1);
}
for(i=0; i < NUM_THREADS; i++)
pthread_join(threads[i], NULL);
printf("Saldo final: %d\n", saldo);
return 0;
}
Italo Valcy Programação em C, 2013.1 33 / 34
Referência
https://computing.llnl.gov/tutorials/pthreads/
Italo Valcy Programação em C, 2013.1 34 / 34
Exercício
Multiplicação de matrizes de forma paralela.
Multiplicação de matriz : A.B=C
O calculo de um elemento da Matriz C, envolve a
multiplicação duma linha de Matrizx A com a coluna da
Matriz B
Exer09: Fazer um programa que recebe duas
matrizes de tamanho variável (MxP e PxQ) como
entrada e calcula a multiplicação entre elas em 2
threads diferentes
++
++
=
hdfcgdec
hbfagbea
hg
fe
dc
ba
....
....
Slide 1
Slide 2
Slide 3
Slide 4
Slide 5
Slide 6
Slide 7
Slide 8
Slide 9
Slide 10
Slide 11
Slide 12
Slide 13
Slide 14
Slide 15
Slide 16
Slide 17
Slide 18
Slide 19
Slide 20
Slide 21
Slide 22
Slide 23
Slide 24
Slide 25
Slide 26
Slide 27
Slide 28
Slide 29
Slide 30
Slide 31
Slide 32
Slide 33
Slide 34