Concorrência e Multithreading em C++: do A ao Z

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

Introdução à concorrência e multithreading em C++



Os processadores modernos possuem múltiplos núcleos e, para usar toda a sua potência, os programas devem ser capazes de executar várias tarefas simultaneamente. Concorrência (concurrency) é a capacidade de um programa executar várias tarefas em períodos de tempo sobrepostos, e multithreading é uma das formas de implementá-la, onde o programa é dividido em várias threads trabalhando em paralelo.



Em C++, o suporte a multithreading foi introduzido no padrão C++11 e desde então tem sido ativamente desenvolvido. Neste artigo, abordaremos todos os aspectos principais: desde a criação de threads até padrões complexos de sincronização.



Fundamentos: criação e gerenciamento de threads



std::thread — o bloco de construção básico



A classe std::thread permite executar uma função ou objeto chamável em uma thread separada. Vejamos um exemplo simples:



#include <iostream>#include <thread>

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

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


O método join() faz a thread principal aguardar a conclusão da thread filha. A alternativa é detach(), que permite que a thread funcione de forma independente, mas requer cuidado especial (a thread pode sobreviver aos objetos que a criaram).



Passagem de argumentos e funções lambda



É possível passar argumentos para as threads e usar lambdas:



#include <thread>#include <string>

void process(int id, const std::string& data) { // Processamento de dados}

int main() { std::thread t1(process, 1, "dados"); std::thread t2([](int x) { // Expressão lambda }, 42); t1.join(); t2.join();}


Sincronização de threads: protegendo dados compartilhados



Quando várias threads acessam os mesmos dados, ocorre uma condição de corrida (race condition). Para evitá-la, são usados mecanismos de sincronização.



Mutexes (std::mutex)



Um mutex é um primitivo de sincronização que garante que apenas uma thread pode possuí-lo a qualquer momento:



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

std::mutex mtx;int counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; // Acesso protegido }}

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


std::lock_guard é um wrapper RAII que captura automaticamente o mutex ao ser criado e o libera ao ser destruído. Isso protege contra o esquecimento acidental de liberar o mutex.



Variáveis de condição (std::condition_variable)



Variáveis de condição permitem que as threads aguardem a ocorrência de um determinado evento:



#include <queue>#include <condition_variable>

std::queue<int> data_queue;std::mutex queue_mutex;std::condition_variable cv;

void producer() { for (int i = 0; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(i); cv.notify_one(); // Notifica o consumidor }}

void consumer() { while (true) { std::unique_lock<std::mutex> lock(queue_mutex); cv.wait(lock, []{ return !data_queue.empty(); }); int value = data_queue.front(); data_queue.pop(); lock.unlock(); // Processa

Blogs

Book Recommendations