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 de synchronisation des threads en C++:

  • Mutex (Mutual Exclusion): Assurent une exclusion mutuelle. Un seul thread peut posséder un mutex à la fois. Utilisés pour protéger les ressources partagées contre les accès simultanés.

    #include <mutex>
    
    std::mutex myMutex;
    
    void criticalSection() {
        std::lock_guard<std::mutex> lock(myMutex); // Acquisition du mutex
        // Travail avec la ressource partagée
    } // Le mutex est automatiquement libéré à la sortie du scope
    
  • Sémaphores: Contrôlent l'accès à un nombre limité de ressources. Un sémaphore a un compteur indiquant le nombre de ressources disponibles. Les threads peuvent diminuer (pour obtenir une ressource) ou augmenter (pour libérer).

    #include <semaphore.h> // Généralement utilisé dans les systèmes POSIX
    
    sem_t mySemaphore;
    
    void initSemaphore(int count) {
        sem_init(&mySemaphore, 0, count); // Initialisation du sémaphore avec le compteur count
    }
    
    void acquireResource() {
        sem_wait(&mySemaphore); // Diminution du compteur, blocage si zéro
        // Utilisation de la ressource
    }
    
    void releaseResource() {
        sem_post(&mySemaphore); // Augmentation du compteur
    }
    
  • Variables de condition: Permettent aux threads d'attendre qu'une certaine condition soit remplie. Généralement utilisées avec des mutex. Un thread en attente libère le mutex et se bloque jusqu'à ce qu'un autre thread le notifie que la condition est remplie.

    #include <condition_variable>
    #include <mutex>
    #include <queue>
    
    std::condition_variable myCondition;
    std::mutex myMutex;
    std::queue<int> myQueue;
    
    void producer(int value) {
        std::lock_guard<std::mutex> lock(myMutex);
        myQueue.push(value);
        myCondition.notify_one(); // Notifie un thread en attente
    }
    
    int consumer() {
        std::unique_lock<std::mutex> lock(myMutex);
        myCondition.wait(lock, [&]{ return !myQueue.empty(); }); // Attente de la condition
        int value = myQueue.front();
        myQueue.pop();
        return value;
    }
    
  • Variables atomiques: Permettent d'effectuer des opérations simples (lecture, écriture, modification) de manière atomique, sans avoir besoin de mutex pour ces opérations spécifiques.

    #include <atomic>
    
    std::atomic_int atomicCounter(0);
    
    void incrementCounter() {
        atomicCounter++; // Incrémentation atomique
    }
    
  • Barrières: Synchronisent plusieurs threads pour qu'aucun ne puisse continuer jusqu'à ce que tous aient atteint la barrière.

    #include <barrier> // C++20
    
    std::barrier myBarrier(3); // Barrière pour 3 threads
    
    void workerThread() {
        // Exécution d'une partie du travail
        myBarrier.arrive_and_wait(); // Attente que tous atteignent la barrière
        // Exécution de la partie suivante du travail
    }
    

Le choix d'un mécanisme spécifique dépend de la nature de l'interaction entre les threads:

  • Mutex: La méthode la plus courante pour protéger les données partagées.
  • Sémaphores: Gestion de l'accès à des ressources limitées.
  • Variables de condition: Attente qu'un événement ou un état spécifique se produise.
  • Variables atomiques: Exécution efficace d'opérations simples et atomiques.
  • Barrières: Synchronisation des threads pour passer ensemble à l'étape suivante.