Sobes.tech
Middle

Hoe kan de interactie tussen threads gesynchroniseerd worden?

sobes.tech AI

Antwoord van AI

Er zijn verschillende hoofdmechanismen voor thread-synchronisatie in C++:

  • Mutexen: Zorgt voor onderlinge uitsluiting. Slechts één thread kan op elk moment een mutex bezitten. Wordt gebruikt om gedeelde bronnen te beschermen tegen gelijktijdige toegang.

    #include <mutex>
    
    std::mutex myMutex;
    
    void criticalSection() {
        std::lock_guard<std::mutex> lock(myMutex); // Mutex verkrijgen
        // Werken met gedeelde bron
    } // Mutex wordt automatisch vrijgegeven bij het verlaten van scope
    
  • Semaforen: Beheren toegang tot beperkte bronnen. Een semaphore heeft een teller die aangeeft hoeveel bronnen beschikbaar zijn. Threads kunnen de teller verlagen (om een bron te verkrijgen) of verhogen (om vrij te geven).

    #include <semaphore.h> // Wordt meestal gebruikt in POSIX-systemen
    
    sem_t mySemaphore;
    
    void initSemaphore(int count) {
        sem_init(&mySemaphore, 0, count); // Initialiseer semaphore met teller count
    }
    
    void acquireResource() {
        sem_wait(&mySemaphore); // Verlaag teller, blokkeer bij 0
        // Gebruik van resource
    }
    
    void releaseResource() {
        sem_post(&mySemaphore); // Verhoog teller
    }
    
  • Voorwaardelijke variabelen: Stellen threads in staat te wachten op een bepaalde voorwaarde. Worden meestal samen met mutexen gebruikt. Een wachtende thread geeft de mutex vrij en wordt geblokkeerd totdat een andere thread deze informeert dat de voorwaarde is voldaan.

    #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(); // Informeer één wachtende thread
    }
    
    int consumer() {
        std::unique_lock<std::mutex> lock(myMutex);
        myCondition.wait(lock, [&]{ return !myQueue.empty(); }); // Wacht op voorwaarde
        int value = myQueue.front();
        myQueue.pop();
        return value;
    }
    
  • Atomische variabelen: Stellen eenvoudige operaties (lezen, schrijven, wijzigen) atomair uit te voeren, zonder dat mutexen voor deze specifieke operaties nodig zijn.

    #include <atomic>
    
    std::atomic_int atomicCounter(0);
    
    void incrementCounter() {
        atomicCounter++; // Atomair verhogen
    }
    
  • Barrières: Synchroniseren meerdere threads zodat geen enkele verder kan gaan totdat alle de barrière hebben bereikt.

    #include <barrier> // C++20
    
    std::barrier myBarrier(3); // Barrière voor 3 threads
    
    void workerThread() {
        // Deel van werk uitvoeren
        myBarrier.arrive_and_wait(); // Wachten tot alle threads de barrière bereiken
        // Uitvoeren van het volgende deel van het werk
    }
    

De keuze van een specifiek mechanisme hangt af van de aard van de interactie tussen de threads:

  • Mutexen: De meest gebruikte manier om gedeelde gegevens te beschermen.
  • Semaforen: Beheer van toegang tot beperkte bronnen.
  • Voorwaardelijke variabelen: Wachten op het optreden van een gebeurtenis of toestand.
  • Atomische variabelen: Efficiënte uitvoering van eenvoudige, atomische operaties.
  • Barrières: Synchroniseren van threads voor gezamenlijke overgang naar de volgende fase.