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 3: Download concorrente

Até agora, os exemplos eram CPU-bound: dividíamos dados entre threads para processar mais rápido. Esse é diferente. O gargalo aqui é I/O, e a concorrência melhora a vazão: várias operações lentas acontecem ao mesmo tempo, como discutido no Capítulo 1.

Para simular downloads sem depender de rede, cada thread “baixa” um arquivo dormindo por um tempo proporcional ao “tamanho” do arquivo:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <time.h>

typedef struct {
    const char *filename;
    int         size_kb;
} Download;

void *download_file(void *arg) {
    Download *d = arg;
    printf("[start] %s (%d KB)\n", d->filename, d->size_kb);

    usleep(d->size_kb * 100);

    printf("[done]  %s\n", d->filename);
    return NULL;
}

int main(void) {
    Download files[] = {
        {"relatorio.pdf",  4200},
        {"foto.jpg",       1800},
        {"dados.csv",      3500},
        {"backup.tar.gz",  7200},
        {"config.json",      50},
    };
    int n = sizeof(files) / sizeof(files[0]);

    struct timespec t0, t1;
    clock_gettime(CLOCK_MONOTONIC, &t0);

    // Versão sequencial: baixa um de cada vez
    for (int i = 0; i < n; i++)
        download_file(&files[i]);

    clock_gettime(CLOCK_MONOTONIC, &t1);
    long seq_ms = (t1.tv_sec - t0.tv_sec) * 1000
                + (t1.tv_nsec - t0.tv_nsec) / 1000000;

    printf("\nSequencial: %ld ms\n\n", seq_ms);

    // Versão concorrente: baixa todos ao mesmo tempo
    clock_gettime(CLOCK_MONOTONIC, &t0);

    pthread_t tids[n];
    for (int i = 0; i < n; i++)
        pthread_create(&tids[i], NULL, download_file, &files[i]);
    for (int i = 0; i < n; i++)
        pthread_join(tids[i], NULL);

    clock_gettime(CLOCK_MONOTONIC, &t1);
    long par_ms = (t1.tv_sec - t0.tv_sec) * 1000
                + (t1.tv_nsec - t0.tv_nsec) / 1000000;

    printf("\nConcorrente: %ld ms\n", par_ms);
}

Código 5-4: Download sequencial vs. concorrente.

O programa roda o mesmo conjunto de downloads duas vezes: primeiro sequencialmente, depois com uma thread por arquivo.

[start] relatorio.pdf (4200 KB)
[done]  relatorio.pdf
[start] foto.jpg (1800 KB)
[done]  foto.jpg
[start] dados.csv (3500 KB)
[done]  dados.csv
[start] backup.tar.gz (7200 KB)
[done]  backup.tar.gz
[start] config.json (50 KB)
[done]  config.json

Sequencial: 1677 ms

[start] relatorio.pdf (4200 KB)
[start] foto.jpg (1800 KB)
[start] dados.csv (3500 KB)
[start] backup.tar.gz (7200 KB)
[start] config.json (50 KB)
[done]  config.json
[done]  foto.jpg
[done]  dados.csv
[done]  relatorio.pdf
[done]  backup.tar.gz

Concorrente: 722 ms

Na versão sequencial, cada download precisa terminar antes do próximo começar, e o tempo total é a soma dos tempos individuais. Na versão concorrente, todos começam praticamente ao mesmo tempo, e o tempo total fica limitado pelo download mais lento (backup.tar.gz, 722 ms).

Esse exemplo é estruturalmente diferente dos dois anteriores: não tem fase de combinação. Cada thread faz seu trabalho e termina. O join existe apenas para garantir que o programa não encerre antes de todas as threads finalizarem.