Sobes.tech
Middle

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.