Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Exemplo 1: Soma paralela de um array

Vamos começar com um problema simples: somar todos os elementos de um array grande. A versão sequencial é trivial:

#include <stdio.h>

#define N 1000

int main(void) {
    int numbers[N];
    for (int i = 0; i < N; i++)
        numbers[i] = i + 1;

    long total = 0;
    for (int i = 0; i < N; i++)
        total += numbers[i];

    printf("total = %ld\n", total);
}

Código 5-1: Soma sequencial de um array.

O resultado é \( \frac{1000 \cdot 1001}{2} = 500500 \).

Paralelizando com threads

A ideia é simples: dividimos o array em fatias (slices) disjuntas e damos cada fatia pra uma thread diferente. Cada thread calcula a soma parcial da sua fatia. Ao final, a thread principal coleta os resultados via pthread_join e soma tudo.

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define N_THREADS 5
#define N_ELEMS   1000

typedef struct {
    const int *data;
    size_t     len;
} Slice;

void *partial_sum(void *arg) {
    Slice *s = arg;
    long sum = 0;
    for (size_t i = 0; i < s->len; i++)
        sum += s->data[i];
    return (void *)sum;
}

int main(void) {
    int numbers[N_ELEMS];
    for (int i = 0; i < N_ELEMS; i++)
        numbers[i] = i + 1;

    size_t chunk = N_ELEMS / N_THREADS;

    pthread_t tids[N_THREADS];
    Slice     slices[N_THREADS];

    for (int t = 0; t < N_THREADS; t++) {
        slices[t].data = numbers + t * chunk;
        slices[t].len  = (t == N_THREADS - 1)
                       ? N_ELEMS - t * chunk
                       : chunk;
        pthread_create(&tids[t], NULL, partial_sum, &slices[t]);
    }

    long total = 0;
    for (int t = 0; t < N_THREADS; t++) {
        void *res;
        pthread_join(tids[t], &res);
        total += (long)res;
    }

    printf("total = %ld\n", total);
}

Código 5-2: Soma paralela usando 5 threads.

O struct Slice armazena um ponteiro para o início de uma fatia do array e o comprimento dessa fatia. Com isso, cada thread sabe exatamente quais elementos deve somar, sem acessar dados de outras threads.

A função partial_sum recebe uma Slice, calcula a soma dos elementos e retorna o resultado. O retorno é feito convertendo um long para void *, truque que funciona pois em plataformas de 64 bits, void * e long têm o mesmo tamanho (8 bytes)1.

No main, dividimos o array de 1000 elementos em 5 fatias de 200 cada. A última fatia recebe os elementos restantes caso a divisão não seja exata. Criamos 5 threads, cada uma recebendo sua Slice, e depois coletamos os resultados com pthread_join.

O que faz esse exemplo funcionar sem nenhuma sincronização: as threads operam sobre regiões disjuntas da memória. Cada thread lê apenas a sua fatia (que é const) e escreve numa variável local sum. A única comunicação acontece no join, quando a thread principal coleta os resultados.


  1. Converter entre long e void * é uma prática comum em código baseado em Pthreads, mas tecnicamente é um comportamento definido pela implementação (não pelo padrão C). Em plataformas de 64 bits (Linux x86-64, ARM64), isso funciona pois ambos os tipos têm 8 bytes. Para código portável, a alternativa é alocar memória no heap e retornar um ponteiro, como fizemos no capítulo anterior. ↩