Concorrência e Multithreading em C++: Abordagens Modernas e Exemplos

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Concorrência e Multithreading em C++: dos fundamentos aos padrões modernos



As aplicações modernas cada vez mais exigem a execução simultânea de várias tarefas. Seja processando requisições de usuários em um servidor, renderizando gráficos ou trabalhando com big data — concorrência e multithreading em C++ se tornaram habilidades obrigatórias para o desenvolvedor. Neste artigo, vamos abordar os principais mecanismos disponíveis na biblioteca padrão do C++, começando com C++11 e terminando com C++20. Você aprenderá como criar threads, sincronizá-las e evitar erros típicos.



Se você já conhece os fundamentos do C++, mas quer se aprofundar em paralelismo — este material é para você. Não vamos nos aprofundar na teoria de sistemas operacionais, mas sim nos concentrar em ferramentas práticas: std::thread, std::mutex, std::async e operações atômicas.



1. Fundamentos do multithreading: std::thread e gerenciamento de threads



No C++11, surgiu o suporte padrão para threads através da classe std::thread. Você pode criar uma nova thread passando um objeto chamável ao construtor — uma função, lambda ou functor.



#include <iostream>#include <thread>

void hello() { std::cout << "Olá da thread " << std::this_thread::get_id() << std::endl;}

int main() { std::thread t(hello); // Criação da thread t.join(); // Aguarda a conclusão da thread return 0;}


É importante sempre chamar join() ou detach() para cada thread criada; caso contrário, ao destruir o objeto std::thread, o programa será encerrado abruptamente. join() bloqueia a thread atual até que a thread filha termine, enquanto detach() permite que a thread execute de forma independente.



Para passar argumentos para a thread, use parâmetros adicionais no construtor:



#include <thread>#include <string>

void print_sum(int a, int b, const std::string& msg) { std::cout << msg << ": " << (a + b) << std::endl;}

int main() { std::thread t(print_sum, 3, 4, "Soma"); t.join(); return 0;}


Observe: os argumentos são copiados para dentro da thread. Se precisar passar uma referência, use std::ref().



2. Sincronização de threads: mutexes e locks



Ao trabalhar com dados compartilhados de diferentes threads, ocorre uma condição de corrida (data race). Para evitá-la, utiliza-se sincronização. A ferramenta principal é std::mutex.



#include <iostream>#include <thread>#include <mutex>

std::mutex mtx;int shared_counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { mtx.lock(); ++shared_counter; mtx.unlock(); }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Resultado: " << shared_counter << std::endl; return 0;}


No entanto, chamar manualmente lock() e unlock() é propenso a erros: se uma exceção ocorrer entre eles, o mutex permanecerá bloqueado. É melhor usar o wrapper RAII std::lock_guard ou std::unique_lock:



void increment() {    for (int i = 0; i < 100000; ++i) {        std::lock_guard<std::mutex> lock(mtx);        ++shared_counter;    }  // desbloqueia automaticamente ao sair do escopo}


Para um gerenciamento mais flexível (por exemplo, bloqueio condicional), use std::unique_lock. E se precisar sincronizar vários mutexes sem deadlock, utilize std::lock().



3. Tarefas assíncronas: std::async e std::future



Nem sempre é conveniente gerenciar threads manualmente.

Blogs

Book Recommendations