C++: Noções Básicas de Multithreading

Última atualização: 2026-08-26

Na aula 42, aprendemos sobre expressões regulares.

Agora, vamos entrar no território avançado do C++ — programação multithreaded.

Computadores modernos são todos multi-core, mas programas single-threaded só podem usar um core — que desperdício.

Multithreading permite que você faça múltiplas coisas ao mesmo tempo, melhorando drasticamente o desempenho.


1. Visão Geral de Multithreading

(1) 1.1 O Que É uma Thread?

Uma thread é a menor unidade de execução de um programa.

Processo vs Thread:

Analogia do mundo real:


(2) 1.2 Por Que Usar Multithreading?

Vantagem Descrição
Desempenho melhorado Computação paralela multi-core
Maior responsividade Thread de UI não bloqueia
Design simplificado Atribuir tarefas diferentes a threads diferentes


2. Criando Threads

(1) 2.1 Uso Básico

O C++11 fornece a classe std::thread no header thread.

Exemplo: Criando uma thread (Dificuldade ⭐)

▶ Exemplo 1: Demo de programação multithreading (Dificuldade ⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <thread>

void hello() {
 std::cout << "Hello from thread!" << std::endl;
}

int main() {
 std::thread t(hello); // Criar thread
 t.join(); // Esperar a thread terminar
 
 std::cout << "Main thread ends" << std::endl;
 return 0;
}

Saída:

TEXT 📖 Somente leitura
Main thread: starting
Main thread: waiting for child thread
Child thread: Hello from thread
Main thread: ended

Resultado da execução:

TEXT 📖 Somente leitura
Hello from thread!
Main thread ends

(2) 2.2 join vs detach

Função Propósito Descrição
join() Esperar a thread terminar Bloqueia a thread atual
detach() Desanexar thread Thread executa independentemente, não pode mais ser joined

Exemplo: Esperando com join (Dificuldade ⭐)

CPP
#include <iostream>
### ▶ Exemplo 2: Demo de programação multithreading (Dificuldade ⭐)

#include <thread>
#include <chrono>

void worker(int id) {
 for (int i = 0; i < 3; i++) {
 std::cout << "Worker " << id << " working..." << std::endl;
 std::this_thread::sleep_for(std::chrono::milliseconds(500));
 }
}

int main() {
 std::thread t1(worker, 1);
 std::thread t2(worker, 2);
 
 t1.join(); // Esperar t1 terminar
 t2.join(); // Esperar t2 terminar
 
 std::cout << "All workers done" << std::endl;
 return 0;
}


3. Passando Argumentos para Threads

(1) 3.1 Passando Parâmetros

O construtor std::thread pode aceitar qualquer objeto chamável e argumentos.

Exemplo: Passando parâmetros (Dificuldade ⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <thread>
#include <string>

void printMessage(std::string msg, int count) {
 for (int i = 0; i < count; i++) {
 std::cout << msg << std::endl;
 }
}

int main() {
 std::thread t(printMessage, "Hello", 3);
 t.join();
 
 return 0;
}

(2) 3.2 Passagem por Referência

Por padrão, argumentos são passados por valor. Para passar por referência, você deve usar std::ref.

Exemplo: Passagem por referência (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <functional>

void increment(int& x) {
 x++;
}

int main() {
 int counter = 0;
 std::thread t(increment, std::ref(counter));
 t.join();
 
 std::cout << "Counter: " << counter << std::endl; // Saída: 1
 return 0;
}


4. Mutexes

(1) 4.1 Por Que Precisamos de Mutexes?

Problema: Múltiplas threads acessando dados compartilhados simultaneamente causa condições de corrida.

Exemplo: Condição de corrida (Dificuldade ⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <thread>
#include <vector>

int counter = 0;

void increment() {
 for (int i = 0; i < 1000; i++) {
 counter++; // Múltiplas threads modificando simultaneamente, resultado é incerto
 }
}

int main() {
 std::vectorstd::thread threads;
 for (int i = 0; i < 10; i++) {
 threads.emplace_back(increment);
 }
 
 for (auto& t : threads) {
 t.join();
 }
 
 std::cout << "Counter: " << counter << std::endl; // Esperado 10000, real pode ser menor
 return 0;
}

(2) 4.2 Protegendo Dados Compartilhados com um Mutex

Um mutex (Mutex) garante que apenas uma thread acesse dados compartilhados por vez.

Exemplo: Protegendo com mutex (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <vector>
#include <mutex>

int counter = 0;
std::mutex mtx;

void increment() {
 for (int i = 0; i < 1000; i++) {
 mtx.lock(); // Bloquear
 counter++;
 mtx.unlock(); // Desbloquear
 }
}

int main() {
 std::vectorstd::thread threads;
 for (int i = 0; i < 10; i++) {
 threads.emplace_back(increment);
 }
 
 for (auto& t : threads) {
 t.join();
 }
 
 std::cout << "Counter: " << counter << std::endl; // Sempre 10000
 return 0;
}

(3) 4.3 lock_guard — Estilo RAII

Recomendado: Use std::lock_guard para bloqueio/desbloqueio automático.

TEXT 📖 Somente leitura
void increment() {
 for (int i = 0; i < 1000; i++) {
 std::lock_guardstd::mutex lock(mtx); // Bloqueia na construção, desbloqueia na destruição
 counter++;
 } // Desbloqueia automaticamente
}


5. Variáveis de Condição

(1) 5.1 Por Que Precisamos de Variáveis de Condição?

Problema: Uma thread precisa esperar que uma condição se torne verdadeira (ex.: fila não está vazia).

Solução: std::condition_variable


(2) 5.2 Exemplo: Produtor-Consumidor (Dificuldade ⭐⭐⭐)

CPP
#include <iostream>
#include <thread>
#include <queue>
#include <mutex>
#include <condition_variable>

std::queueint q;
std::mutex mtx;
std::condition_variable cv;

void producer() {
 for (int i = 0; i < 10; i++) {
 std::lock_guardstd::mutex lock(mtx);
 q.push(i);
 std::cout << "Produced: " << i << std::endl;
 cv.notify_one(); // Notificar consumidor
 }
}

void consumer() {
 for (int i = 0; i < 10; i++) {
 std::unique_lockstd::mutex lock(mtx);
 cv.wait(lock, { return !q.empty(); }); // Esperar até a fila não estar vazia
 
 int value = q.front();
 q.pop();
 std::cout << "Consumed: " << value << std::endl;
 }
}

int main() {
 std::thread t1(producer);
 std::thread t2(consumer);
 
 t1.join();
 t2.join();
 
 return 0;
}

Saída:

TEXT 📖 Somente leitura
Produced: 
Consumed: 


6. Tarefas Assíncronas

(1) 6.1 std::async

std::async é usado para lançar uma tarefa assíncrona e retorna um std::future.

Exemplo: Computação assíncrona (Dificuldade ⭐⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <future>

int calculate(int x) {
 return x * x;
}

int main() {
 std::futureint result = std::async(calculate, 10);
 std::cout << "Result: " << result.get() << std::endl; // Saída: 100
 
 return 0;
}

❓ Perguntas Frequentes

P: Quantas threads devo usar? R: Tipicamente igual ao número de cores da CPU. Threads demais causam overhead de troca de contexto.


P: O que é um deadlock? R: Duas threads esperando uma pela outra liberar locks, então nenhuma pode continuar.

Como evitar:


P: std::thread ou OpenMP — qual é melhor? R: - std::thread: Flexível, multiplataforma - OpenMP: Simples, bom para computação científica


▶ Exemplo 3: Criando threads (Dificuldade ⭐)

CPP
#include <iostream>
#include <thread>

void printNumbers(int start, int end) {
    for (int i = start; i <= end; i++) {
        std::cout << i << " ";
    }
    std::cout << std::endl;
}

int main() {
    std::thread t1(printNumbers, 1, 5);
    std::thread t2(printNumbers, 10, 15);

    t1.join();
    t2.join();

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
 

Exemplo de saída:

TEXT 📖 Somente leitura
1 10 2 11 3 12 4 13 5 14 15
💡 Dica: std::thread cria threads, join() espera que terminem. A ordem da saída pode variar devido ao escalonamento de threads.


Ponto-Chave Resumo
std::thread Criar threads
join/detach Esperar/desanexar threads
std::mutex Mutex, protege dados compartilhados
std::lock_guard Bloqueio estilo RAII
std::condition_variable Variável de condição, comunicação entre threads
std::async Tarefas assíncronas

📖 Resumo

📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie duas threads que exibam "Thread A" e "Thread B" respectivamente, e observe a aleatoriedade da ordem de saída.

  2. Intermediário (Dificuldade ⭐⭐): Crie 4 threads, cada uma computando uma soma acumulada sobre um intervalo de números (ex.: 1-2500, 2501-5000...), depois combine os resultados no final.

  3. Desafio (Dificuldade ⭐⭐⭐): Use std::async e std::future para implementar um simulador de download concorrente: crie 3 tarefas assíncronas, cada uma simulando o download de um arquivo de tamanho diferente, e espere todas terminarem antes de resumir.



Próxima aula: Sincronização de Multithreading (#44)

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%