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 <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
- Utilisation
condition_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.