Concorrência em C++¶
C++ combina threads, mutexes, variáveis de condição, futures, atomics e modelo formal de memória. Uma corrida de dados em memória comum causa comportamento indefinido.
Vida da thread¶
std::jthread é uma thread C++20 que solicita parada e faz join na destruição.
Cancelamento cooperativo exige observar o stop token.
void consume(std::stop_token stop, work_queue& queue) {
while (!stop.stop_requested()) {
if (auto item = queue.try_pop()) {
process(*item);
}
}
}
std::jthread worker{consume, std::ref(queue)};
O estado referenciado deve sobreviver aos acessos. Join não corrige referência já dangling.
Invariante protegido por mutex¶
class counter {
public:
void increment() {
std::lock_guard guard{mutex_};
++value_;
}
[[nodiscard]] std::uint64_t value() const {
std::lock_guard guard{mutex_};
return value_;
}
private:
mutable std::mutex mutex_;
std::uint64_t value_{};
};
Mutex e estado têm um proprietário e todos os acessos seguem o protocolo. Várias chamadas bloqueadas individualmente não formam operação composta atômica.
Variáveis de condição¶
Espere com predicado por causa de despertares espúrios e concorrência:
O predicado lê estado protegido pelo mesmo mutex. A notificação não é evento armazenado.
Atomics e ordenação¶
std::atomic<T> oferece operações atômicas. Ordem sequencialmente consistente é
mais simples. Ordens fracas exigem prova ligando sincronização, vida e todos os
acessos não atômicos. Atomicidade de uma variável não cobre invariante multivariável.
Lock-free não significa wait-free, mais rápido nem simples.
volatile não sincroniza¶
Em C++ portável, volatile não torna operações atômicas nem cria ordenação entre
threads. Tem usos especializados conforme contratos da plataforma.
Computação paralela¶
Ajuda quando o trabalho é grande e independente. Fração sequencial, escalonamento, contenção, false sharing, cache, largura de banda e oversubscription limitam a aceleração.
Exercícios¶
- Explique a corrida em duas threads com
++ordinary_counter. - Projete uma fila bloqueante limitada.
- Explique por que shared pointer protege posse, não o estado apontado.