Scrierea codului concurent și multithreading de înaltă performanță în C++ necesită o înțelegere riguroasă atât a abstracțiunilor de nivel înalt (thread-uri, mutex-uri, lock-uri), cât și a comportamentului fizic al hardware-ului (reordonarea instrucțiunilor de către compilator și procesor, bariere de memorie și ierarhia de cache-uri CPU). Începând cu standardul C++20, lucrul cu firele de execuție a devenit mult mai sigur prin introducerea clasei std::jthread și a semnalelor cooperative de oprire.
1. std::jthread: Înlocuitorul Modern și Sigur pentru std::thread
Clasa clasică std::thread (C++11) avea un defect major de design: dacă uitați să apelați .join() înainte ca obiectul să iasă din scope, destructorul invoca imediat std::terminate(), prăbușind întreg programul! std::jthread rezolvă acest neajuns executând automat join() în destructor și oferă suport integrat pentru anulare cooperativă prin std::stop_token:
#include <thread>
#include <chrono>
#include <print>
void workerProcesare(std::stop_token stopToken, int workerId) {
std::println("Worker-ul {} a pornit.", workerId);
// Firul rulează continuu până când i se cere oprirea:
while (!stopToken.stop_requested()) {
std::println("Worker-ul {} proceseaza date...", workerId);
std::this_thread::sleep_for(std::chrono::milliseconds(200));
}
std::println("Worker-ul {} a interceptat cererea de oprire si s-a incheiat curat.", workerId);
}
int main() {
{
// Lansăm un jthread (trece automat stop_token ca prim parametru):
std::jthread t(workerProcesare, 1);
std::this_thread::sleep_for(std::chrono::milliseconds(600));
std::println("Main cere oprirea worker-ului...");
t.request_stop(); // Trimite semnalul de oprire cooperativ
// LA IEȘIREA DIN SCOPE: t.~jthread() apelează automat t.join()!
}
std::println("Programul principal s-a incheiat in siguranta.");
return 0;
}
2. Modelul de Memorie C++ (Memory Model) și std::memory_order
Când mai multe fire de execuție citesc și scriu date partajate fără un mutex, se utilizează tipurile atomice std::atomic<T>. Pentru performanță maximă, putem controla precis gradul de sincronizare a memoriei prin parametrul std::memory_order:
3. Fenomenul de False Sharing și Evitarea Sa
Dacă două variabile atomice utilizate intens de două thread-uri diferite se află pe aceeași linie de cache (Cache Line – 64 octeți), nucleele CPU își vor invalida reciproc cache-ul L1 la fiecare scriere, degradând viteza de până la 50 de ori. Soluția este alinierea explicită a fiecărei variabile la dimensiunea liniei de cache hardware:
#include <new>
struct alignas(std::hardware_destructive_interference_size) ContorIzolat {
std::atomic<uint64_t> valoare{0};
};