Bibliothèque standard C++<condition_variable>

Dans la programmation multithread, la synchronisation entre les threads est un problème très important.

La norme C++11 a introduit<condition_variable>Le fichier d'en-tête fournit un mécanisme qui permet aux threads de se suspendre lorsque certaines conditions ne sont pas remplies, jusqu'à ce que d'autres threads leur notifient que les conditions ont été satisfaites.

condition_variableC'est un outil avancé de synchronisation entre les threads, plus sûr et plus pratique que l'utilisation de primitives de synchronisation de bas niveau (telles que les mutex et les variables de condition).

condition_variableIl s'agit d'un modèle de classe utilisé pour implémenter la synchronisation entre les threads dans un environnement multithread. Il permet à un ou plusieurs threads d'attendre qu'une certaine condition devienne vraie, tandis que d'autres threads peuvent réveiller ces threads en attente.

Syntaxe

Les suivantes sontcondition_variablesyntaxe de base :

#include <condition_variable>

void notify_one() {
    // 唤醒一个等待的线程
}

void notify_all() {
    // 唤醒所有等待的线程
}

template <class Mutex>
class condition_variable {
public:
    condition_variable();
    ~condition_variable();

    void wait(unique_lock<mutex>& lock);
    void wait_for(unique_lock<mutex>& lock, chrono::duration<Rep, Period> const& rel_time);
    void wait_until(unique_lock<mutex>& lock, chrono::time_point<Clock, Duration> const& abs_time);

    void notify_one() noexcept;
    void notify_all() noexcept;
};

Exemple

Voici un exemple utilisantcondition_variableUn exemple simple de son utilisation, démontrant l'implémentation de base du problème producteur-consommateur.

Exemple

#include <iostream>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> product;

void producer(int id) {
    for (int i = 0; i < 5; ++i) {
        std::unique_lock<std::mutex> lck(mtx);
        product.push(id * 100 + i);
        std::cout << "Producteur " << id << " a produit " << product.back() << std::endl;
        cv.notify_one();
        lck.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
}

void consumer(int id) {
    while (true) {
        std::unique_lock<std::mutex> lck(mtx);
        cv.wait(lck, []{ return !product.empty(); });
        if (!product.empty()) {
            int prod = product.front();
            product.pop();
            std::cout << "Consommateur " << id << " a consommé " << prod << std::endl;
        }
        lck.unlock();
    }
}

int main() {
    std::thread producers[2];
    std::thread consumers[2];

    for (int i = 0; i < 2; ++i) {
        producers[i] = std::thread(producer, i + 1);
    }

    for (int i = 0; i < 2; ++i) {
        consumers[i] = std::thread(consumer, i + 1);
    }

    for (int i = 0; i < 2; ++i) {
        producers[i].join();
        consumers[i].join();
    }

    return 0;
}

La sortie de ce programme peut varier car elle dépend de l'ordonnancement des threads. Cependant, vous verrez le producteur produire des éléments, puis le consommateur les consommer. La sortie sera similaire à :

Producer 1 produced 100
Producer 2 produced 200
Consumer 1 consumed 100
Producer 1 produced 101
Consumer 2 consumed 200
Producer 2 produced 201
Consumer 1 consumed 101
...

Notes

  • Utilisationcondition_variableCe faisant, vous devez vous assurer que le mutex est acquis avant l'attente, et libéré après avoir été réveillé.
  • wait、wait_foretwait_untilLes fonctions libèrent le mutex, puis le réacquièrent pendant l'attente.
  • notify_oneréveille un thread en attente, tandis quenotify_allréveille tous les threads en attente.
autres extensions