Sobes.tech
Middle

Jak można zsynchronizować interakcję między wątkami?

sobes.tech AI

Odpowiedź od AI

Istnieje kilka głównych mechanizmów synchronizacji wątków w C++:

  • Muteksy (Mutexes): Zapewniają wzajemne wykluczanie. Tylko jeden wątek może posiadać mutex w danym momencie. Używane do ochrony wspólnych zasobów przed równoczesnym dostępem.

    #include <mutex>
    
    std::mutex myMutex;
    
    void criticalSection() {
        std::lock_guard<std::mutex> lock(myMutex); // Zdobycie mutexa
        // Praca z zasobem wspólnym
    } // Mutex jest automatycznie zwalniany po wyjściu z zakresu
    
  • Semafory: Kontrolują dostęp do ograniczonej liczby zasobów. Semafor ma licznik wskazujący liczbę dostępnych zasobów. Wątki mogą zmniejszać (aby uzyskać zasób) lub zwiększać (aby zwolnić).

    #include <semaphore.h> // Zazwyczaj używany w systemach POSIX
    
    sem_t mySemaphore;
    
    void initSemaphore(int count) {
        sem_init(&mySemaphore, 0, count); // Inicjalizacja semafora z licznikiem count
    }
    
    void acquireResource() {
        sem_wait(&mySemaphore); // Zmniejsza licznik, blokuje jeśli jest 0
        // Użycie zasobu
    }
    
    void releaseResource() {
        sem_post(&mySemaphore); // Zwiększa licznik
    }
    
  • Zmienne warunkowe (Condition Variables): Pozwalają wątkom oczekiwać na określony warunek. Zazwyczaj używane razem z mutexami. Wątek oczekujący zwalnia mutex i blokuje się do momentu, gdy inny wątek go powiadomi o spełnieniu warunku.

    #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(); // Powiadomienie oczekującego wątku
    }
    
    int consumer() {
        std::unique_lock<std::mutex> lock(myMutex);
        myCondition.wait(lock, [&]{ return !myQueue.empty(); }); // Oczekiwanie na warunek
        int value = myQueue.front();
        myQueue.pop();
        return value;
    }
    
  • Zmienne atomowe (Atomic Variables): Pozwalają na wykonywanie prostych operacji (odczyt, zapis, modyfikacja) atomowo, bez konieczności używania mutexów dla tych operacji.

    #include <atomic>
    
    std::atomic_int atomicCounter(0);
    
    void incrementCounter() {
        atomicCounter++; // Atomowe zwiększenie
    }
    
  • Bariery (Barriers): Synchronizują kilka wątków tak, aby żaden nie mógł kontynuować, dopóki wszyscy nie osiągną bariery.

    #include <barrier> // C++20
    
    std::barrier myBarrier(3); // Bariera dla 3 wątków
    
    void workerThread() {
        // Wykonanie części pracy
        myBarrier.arrive_and_wait(); // Oczekiwanie na wszystkich wątków przy barierze
        // Wykonanie kolejnej części pracy
    }
    

Wybór konkretnego mechanizmu zależy od charakteru interakcji między wątkami:

  • Muteksy: Najczęstszy sposób ochrony wspólnych danych.
  • Semafory: Zarządzanie dostępem do ograniczonych zasobów.
  • Zmienne warunkowe: Oczekiwanie na wystąpienie określonego zdarzenia lub stanu.
  • Zmienne atomowe: Efektywne wykonywanie prostych, atomowych operacji.
  • Bariery: Synchronizacja wątków do wspólnego przejścia do kolejnego etapu.