Concurrence et multithreading en C++ : de A à Z

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction à la concurrence et au multithreading en C++



Les processeurs modernes possèdent plusieurs cœurs, et pour exploiter pleinement leur puissance, les programmes doivent être capables d'exécuter plusieurs tâches simultanément. La concurrence (concurrency) est la capacité d'un programme à exécuter plusieurs tâches dans des intervalles de temps qui se chevauchent, et le multithreading (multithreading) est l'une des façons de la mettre en œuvre, où le programme se divise en plusieurs threads fonctionnant en parallèle.



En C++, le support du multithreading est apparu avec la norme C++11 et n'a cessé de se développer depuis. Dans cet article, nous aborderons tous les aspects clés : de la création de threads aux modèles de synchronisation complexes.



Les bases : création et gestion des threads



std::thread — le bloc de construction de base



La classe std::thread permet de lancer une fonction ou un objet appelable dans un thread séparé. Voici un exemple simple :



#include <iostream>#include <thread>

void hello() { std::cout << "Bonjour depuis le thread " << std::this_thread::get_id() << std::endl;}

int main() { std::thread t(hello); // Lancement du thread t.join(); // Attente de la fin du thread return 0;}


La méthode join() force le thread principal à attendre la fin du thread fils. L'alternative est detach(), qui permet au thread de fonctionner indépendamment, mais nécessite une attention particulière (le thread peut survivre aux objets qui l'ont créé).



Passage d'arguments et fonctions lambda



On peut passer des arguments aux threads et utiliser des lambdas :



#include <thread>#include <string>

void process(int id, const std::string& data) { // Traitement des données}

int main() { std::thread t1(process, 1, "données"); std::thread t2([](int x) { // Expression lambda }, 42); t1.join(); t2.join();}


Synchronisation des threads : protection des données partagées



Lorsque plusieurs threads accèdent aux mêmes données, une condition de concurrence (race condition) apparaît. Pour l'éviter, on utilise des mécanismes de synchronisation.



Mutex (std::mutex)



Un mutex est un primitif de synchronisation qui garantit qu'un seul thread peut le posséder à un moment donné :



#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; // Accès protégé }}

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


std::lock_guard est une enveloppe RAII qui verrouille automatiquement le mutex à la création et le libère à la destruction. Cela protège contre l'oubli accidentel de libération du mutex.



Variables conditionnelles (std::condition_variable)



Les variables conditionnelles permettent aux threads d'attendre la survenue d'un événement spécifique :



#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(); // Notification au consommateur }}

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(); // Trait

Blogs

Book Recommendations