POSIX threads para programação concorrente: create / join, condições de corrida, mutex, deadlock.
<pthread.h> e compile com -pthread.#include <stdio.h> #include <pthread.h> // Ponto de entrada da thread: tanto arg quanto retorno são void* void *worker(void *arg) { int id = *(int *)arg; printf("olá da thread %d\n", id); return NULL; } int main(void) { pthread_t t1, t2; int a = 1, b = 2; pthread_create(&t1, NULL, worker, &a); pthread_create(&t2, NULL, worker, &b); pthread_join(t1, NULL); // espera t1 pthread_join(t2, NULL); printf("main terminou\n"); return 0; }
main pode sair antes das threads terminarem e você vaza recursos. (A menos que você explicitamente destaque a thread.)void *compute(void *arg) { int x = *(int *)arg; int *result = malloc(sizeof(int)); *result = x * x; return result; } // em main: void *ret; pthread_join(t, &ret); printf("resultado = %d\n", *(int *)ret); free(ret);
#include <stdio.h> #include <pthread.h> #define N 4 #define LOOPS 1000000 long counter = 0; // compartilhada void *worker(void *arg) { for (int i = 0; i < LOOPS; i++) { counter++; // NÃO é atômico! } return NULL; } int main(void) { pthread_t th[N]; for (int i = 0; i < N; i++) pthread_create(&th[i], NULL, worker, NULL); for (int i = 0; i < N; i++) pthread_join(th[i], NULL); printf("esperado: %d\n", N * LOOPS); printf("real: %ld\n", counter); return 0; }
counter++ parece atômico, mas no nível da CPU são três passos — carregar, somar, armazenar. Duas threads fazendo isso simultaneamente perdem atualizações.#include <stdio.h> #include <pthread.h> #define N 4 #define LOOPS 1000000 long counter = 0; pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; void *worker(void *arg) { for (int i = 0; i < LOOPS; i++) { pthread_mutex_lock(&mtx); counter++; pthread_mutex_unlock(&mtx); } return NULL; } int main(void) { pthread_t th[N]; for (int i = 0; i < N; i++) pthread_create(&th[i], NULL, worker, NULL); for (int i = 0; i < N; i++) pthread_join(th[i], NULL); printf("counter = %ld\n", counter); // sempre 4000000 pthread_mutex_destroy(&mtx); return 0; }
#include <stdatomic.h> atomic_long counter = 0; // no worker: atomic_fetch_add(&counter, 1); // seguro, lock-free
<stdatomic.h> é C11. Use atômicos para contadores simples e mutexes para proteger invariantes mais complexas.// RUIM: ordens de lock diferentes por thread // Thread A: lock(m1) → lock(m2) // Thread B: lock(m2) → lock(m1) // → as duas seguram um e esperam o outro
pthread_mutex_trylock falha em vez de esperar. Se não conseguir pegar o segundo lock, libere o primeiro e tente de novo.valgrind --tool=helgrind ./app detecta condições de corrida e deadlocks potenciais.pthread_cond_t para esperar quando o buffer está vazio ou cheio.clock_gettime para 1, 2, 4 e 8 threads, depois plote os resultados.Teste seu entendimento desta aula!
pthread_create(&tid, NULL, func, arg) dispara uma nova thread que começa a executar em func.
Código envolvido em pthread_mutex_lock / unlock só pode ser executado por uma thread de cada vez.
pthread_join(tid, &retval) bloqueia até a thread terminar. Pule e os recursos dela não serão recuperados.