Sobes.tech
Middle

Come si può sincronizzare l'interazione tra thread?

sobes.tech AI

Risposta dell'AI

Esistono diversi meccanismi principali di sincronizzazione dei thread in C++:

  • Mutex (Mutua Esclusione): Garantisce l'esclusione mutua. Solo un thread può possedere un mutex alla volta. Viene usato per proteggere risorse condivise da accessi simultanei.

    #include <mutex>
    
    std::mutex myMutex;
    
    void criticalSection() {
        std::lock_guard<std::mutex> lock(myMutex); // Acquisizione del mutex
        // Lavoro con risorsa condivisa
    } // Il mutex viene rilasciato automaticamente all'uscita dallo scope
    
  • Semafori: Controllano l'accesso a risorse limitate. Un semaforo ha un contatore che indica il numero di risorse disponibili. I thread possono decrementare (per ottenere una risorsa) o incrementare (per rilasciare).

    #include <semaphore.h> // Usato tipicamente in sistemi POSIX
    
    sem_t mySemaphore;
    
    void initSemaphore(int count) {
        sem_init(&mySemaphore, 0, count); // Inizializza il semaforo con il contatore count
    }
    
    void acquireResource() {
        sem_wait(&mySemaphore); // Decrementa il contatore, blocca se è zero
        // Uso della risorsa
    }
    
    void releaseResource() {
        sem_post(&mySemaphore); // Incrementa il contatore
    }
    
  • Variabili di condizione: Permettono ai thread di attendere che si verifichi una certa condizione. Sono usate insieme ai mutex. Un thread in attesa rilascia il mutex e si blocca fino a quando un altro thread lo notifica che la condizione è soddisfatta.

    #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(); // Notifica un thread in attesa
    }
    
    int consumer() {
        std::unique_lock<std::mutex> lock(myMutex);
        myCondition.wait(lock, [&]{ return !myQueue.empty(); }); // Attende la condizione
        int value = myQueue.front();
        myQueue.pop();
        return value;
    }
    
  • Variabili atomiche: Permettono di eseguire operazioni semplici (lettura, scrittura, modifica) in modo atomico, senza bisogno di mutex per queste operazioni.

    #include <atomic>
    
    std::atomic_int atomicCounter(0);
    
    void incrementCounter() {
        atomicCounter++; // Incremento atomico
    }
    
  • Barriere: Sincronizzano più thread in modo che nessuno possa continuare finché tutti non hanno raggiunto la barriera.

    #include <barrier> // C++20
    
    std::barrier myBarrier(3); // Barriera per 3 thread
    
    void workerThread() {
        // Esecuzione di parte del lavoro
        myBarrier.arrive_and_wait(); // Attende che tutti raggiungano la barriera
        // Esecuzione della parte successiva del lavoro
    }
    

La scelta di un meccanismo specifico dipende dalla natura dell'interazione tra i thread:

  • Mutex: Il metodo più comune per proteggere dati condivisi.
  • Semafori: Gestione dell'accesso a risorse limitate.
  • Variabili di condizione: Attesa che si verifichi un evento o uno stato specifico.
  • Variabili atomiche: Esecuzione efficiente di operazioni semplici e atomiche.
  • Barriere: Sincronizzazione dei thread per passare insieme alla fase successiva.