Aula 19 - Concorrência de Alto Desempenho com std::thread 🧵
Objetivo Pedagógico
Objetivo: Desenvolvimento multithread de alta performance em C++: criação de threads (std::thread, std::jthread), sincronização com std::mutex, std::condition_variable e operações atômicas sem travas (std::atomic).
📑 1. Fundamentos Teóricos & Análise Técnica
A programação concorrente em C++ moderno oferece controle fino sobre a arquitetura de múltiplos núcleos do processador, operando diretamente sobre o modelo de memória formalizado pelo padrão C++11 (Memory Model).
Mecanismos de concorrência essenciais: 1. std::jthread (C++20): Thread cooperativa que executa join automático em seu destrutor (evitando chamadas acidentais a std::terminate) e suporta cancelamento cooperativo nativo via std::stop_token. 2. Sincronização com Bloqueio: - std::mutex: Trava de exclusão mútua. - std::lock_guard e std::unique_lock: RAII wrappers que garantem a liberação do mutex mesmo que exceções ocorram. - std::condition_variable: Permite que threads durmam sem consumir ciclos de CPU até que um predicado seja satisfeito por outra thread. 3. Programação Lock-Free com std::atomic<T>: Operações em nível de hardware suportadas por instruções atômicas de CPU (como CMPXCHG em x86), permitindo construir contadores, filas e listas sem o overhead de travas de mutex.
📐 Arquitetura Conceitual & Diagrama de Fluxo
graph TD
subgraph LockFree ["Programação Concorrente de Alta Performance"]
Atomic["std::atomic<int> counter"] -->|fetch_add()| Hardware["Instrução Atômica da CPU (LOCK XADD)"]
Hardware --> ZeroLock["Zero Mutex / Zero Troca de Contexto!"]
end
subgraph SincronizacaoRAII ["Sincronização com Bloqueio RAII"]
Mutex["std::mutex"] --> LockGuard["std::lock_guard<std::mutex> lock(m)"]
LockGuard --> CriticalSection["Seção Crítica Protegida"]
end
style LockFree fill:#e8f5e9,stroke:#2e7d32
style SincronizacaoRAII fill:#e3f2fd,stroke:#1565c0 🔍 Pilares e Diretrizes Técnicas
Nesta unidade, aprofundamos os seguintes conceitos fundamentais: - Prevenção de Condições de Corrida (Data Races): Qualquer escrita concorrente em variável compartilhada deve ser protegida por mutex ou ser atômica. - Ordem de Memória (Memory Ordering): Uso de std::memory_order_relaxed, acquire e release para otimização em processadores ARM/x86. - RAII para Mutexes: Uso estrito de std::lock_guard para nunca deixar um mutex permanentemente travado (Deadlock). - Evitar Falso Compartilhamento (False Sharing): Alinhamento de variáveis atômicas em linhas de cache separadas com alignas(64).
🛠️ 2. Implementação Prática em C++ Concorrência, std::thread e Atomics
Abaixo está a implementação técnica de referência, estruturada com padrões de engenharia de software e foco em robustez:
// thread_pool_atomic.cpp (Concorrência Atômica de Alta Performance em C++)
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
// Variável atômica compartilhada (Lock-Free)
std::atomic<long long> global_counter{0};
void worker_increment(int iterations) {
for (int i = 0; i < iterations; i++) {
// Operação atômica em nível de instrução de hardware
global_counter.fetch_add(1, std::memory_order_relaxed);
}
}
int main() {
const int num_threads = 4;
const int ops_per_thread = 250000;
std::vector<std::thread> threads;
for (int i = 0; i < num_threads; i++) {
threads.emplace_back(worker_increment, ops_per_thread);
}
for (auto& t : threads) {
t.join();
}
std::cout << "Contador Final: " << global_counter.load()
<< " (Esperado: " << num_threads * ops_per_thread << ")\n";
return 0;
}
💡 Análise Passo a Passo do Código
- Uso de std::atomic: Impede condições de corrida sem necessitar de travas lentas de mutex.
- Memory Order Relaxed:
std::memory_order_relaxedmaximiza o rendimento em contadores que não exigem sincronização de outras variáveis adjacentes. - Coordenação com join(): A thread principal aguarda o término de todas as trabalhadoras antes de exibir o total.
🎯 3. Próximos Passos & Sequência Didática
-
Slides da Aula
-
Quiz de Fixação
-
Exercícios Práticos
-
Desafio de Projeto