Comment peut-on synchroniser l'interaction entre les threads?
sobes.tech IA
Réponse de l'IA
Il existe plusieurs mécanismes principaux pour la synchronisation et l’interaction entre threads en C/C++:
-
Mutex (Mutual Exclusion): Garantissent un accès exclusif à une ressource partagée. Le thread qui acquiert le mutex bloque les autres jusqu’à ce qu’il le libère.
#include <mutex> std::mutex my_mutex; void my_thread_function() { my_mutex.lock(); // Acquisition du mutex // Travail avec la ressource partagée my_mutex.unlock(); // Libération du mutex }Ou en utilisant RAII:
#include <mutex> #include <lock_guard> std::mutex my_mutex; void my_thread_function() { std::lock_guard<std::mutex> lock(my_mutex); // Acquisition du mutex lors de la création de l’objet // Travail avec la ressource partagée // Le mutex est automatiquement libéré à la sortie du scope } -
Sémaphores: Généralisation des mutex. Maintiennent un compteur permettant à un nombre défini de threads d’accéder simultanément à la ressource.
#include <semaphore.h> // Pour les sémaphores POSIX #include <windows.h> // Pour les sémaphores Windows sem_t my_semaphore; // Sémaphore POSIX void my_thread_function() { sem_wait(&my_semaphore); // Diminuer le compteur du sémaphore, bloquer si 0 // Travail avec la ressource sem_post(&my_semaphore); // Augmenter le compteur du sémaphore }(Remarque : La norme C++ ne comprend pas directement les sémaphores, mais ils sont accessibles via des bibliothèques système ou des implémentations tierces.)
-
Variables de condition: Utilisées pour qu’un thread attende qu’une condition spécifique soit remplie, tout en libérant le mutex. Un autre thread peut notifier que la condition est remplie.
#include <mutex> #include <condition_variable> std::mutex my_mutex; std::condition_variable my_cv; bool condition_met = false; void waiting_thread() { std::unique_lock<std::mutex> lock(my_mutex); my_cv.wait(lock, []{ return condition_met; }); // Attendre que la condition soit remplie // La condition est remplie, travailler avec la ressource } void notifying_thread() { std::unique_lock<std::mutex> lock(my_mutex); condition_met = true; lock.unlock(); // Libérer le mutex avant de notifier (optionnel, mais recommandé) my_cv.notify_one(); // Notifier un thread en attente // ou // my_cv.notify_all(); // Notifier tous les threads en attente } -
Opérations atomiques: Permettent d’effectuer des opérations simples sur des variables (par exemple, incrémentation, décrémentation) comme une action indivisible, sans nécessiter de verrouillage explicite.
#include <atomic> std::atomic<int> counter(0); void my_thread_function() { counter++; // Incrément atomique // Ou: // counter.fetch_add(1); } -
Verrous de lecture-écriture (Shared Mutexes): Permettent à plusieurs threads de lire des données simultanément, mais un seul peut écrire.
#include <shared_mutex> // C++17 et supérieur std::shared_mutex my_shared_mutex; void read_thread() { my_shared_mutex.lock_shared(); // Acquisition pour lecture // Lecture des données my_shared_mutex.unlock_shared(); // Libération pour lecture } void write_thread() { my_shared_mutex.lock(); // Acquisition pour écriture (exclusif) // Écriture des données my_shared_mutex.unlock(); // Libération pour écriture } -
Barrières: Permettent à un groupe de threads d’attendre que tous atteignent un certain point dans leur exécution, avant de continuer.
#include <barrier> // C++20 std::barrier sync_point(4); // Barrière pour 4 threads void my_thread_function() { // Travail avant la barrière sync_point.arrive_and_wait(); // Attendre que tous atteignent la barrière // Travail après la barrière }
Le choix d’un mécanisme spécifique dépend de la nature de l’interaction et des ressources partagées.