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