C++ std::condition_variable 是什么 有什么用 条件变量 线程同步

Posted 软件工程小施同学

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ std::condition_variable 是什么 有什么用 条件变量 线程同步相关的知识,希望对你有一定的参考价值。

一、总述

在C++11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作

当条件不满足时,相关线程被一直阻塞

直到某种条件出现,这些线程才会被唤醒

主要成员函数如下:

二、具体函数:

 1、wait函数:

(1)wait(unique_lock <mutex>&lck)

当前线程的执行会被阻塞,直到收到 notify 为止。

(2)wait(unique_lock <mutex>&lck,Predicate pred)

当前线程仅在pred=false时阻塞;如果pred=true时,不阻塞。

wait()可依次拆分为三个操作:释放互斥锁、等待在条件变量上、再次获取互斥锁

2、notify_one:

notify_one():没有参数、没有返回值。

解除阻塞当前正在等待此条件的线程之一。如果超过一个,不会指定具体哪一线程。

如果没有线程在等待,则该函数不执行任何操作。

// condition_variable::notify_one
#include <iostream>           // std::cout
#include <thread>             // std::thread
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable

std::mutex mtx;
std::condition_variable produce,consume;

int cargo = 0;     // shared value by producers and consumers

void consumer () 
  std::unique_lock<std::mutex> lck(mtx);
  while (cargo==0) consume.wait(lck);
  std::cout << cargo << '\\n';
  cargo=0;
  produce.notify_one();


void producer (int id) 
  std::unique_lock<std::mutex> lck(mtx);
  while (cargo!=0) produce.wait(lck);
  cargo = id;
  consume.notify_one();


int main ()

  std::thread consumers[10],producers[10];
  // spawn 10 consumers and 10 producers:
  for (int i=0; i<10; ++i) 
    consumers[i] = std::thread(consumer);
    producers[i] = std::thread(producer,i+1);
  

  // join them back:
  for (int i=0; i<10; ++i) 
    producers[i].join();
    consumers[i].join();
  

  return 0;

三、分析

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:

(1)、一个线程因等待“条件变量的条件成立”而挂起

(2)、另外一个线程使“条件成立”,给出信号,从而唤醒被等待的线程

1、有什么用:

当需要死循环判断某个条件成立与否时【true or false】,我们往往需要开一个线程死循环来判断,这样非常消耗CPU。

使用条件变量,可以让当前线程wait,释放CPU,如果条件改变时,我们再notify退出线程,再次进行判断。

2、其他解释

想要修改共享变量(即“条件”)的线程必须:
(1). 获得一个std::mutex
(2). 当持有锁的时候,执行修改动作
(3). 对std::condition_variable执行notify_one或notify_all(当做notify动作时,不必持有锁)

即使共享变量是原子性的,它也必须在mutex的保护下被修改,这是为了能够将改动正确发布到正在等待的线程

任意要等待std::condition_variable的线程必须:
(1). 获取std::unique_lock<std::mutex>,这个mutex正是用来保护共享变量(即“条件”)的
(2). 执行wait, wait_for或者wait_until. 这些等待动作原子性地释放mutex,并使得线程的执行暂停
(3). 当获得条件变量的通知,或者超时,或者一个虚假的唤醒,那么线程就会被唤醒,并且获得mutex. 然后线程应该检查条件是否成立,如果是虚假唤醒,就继续等待

【注: 所谓虚假唤醒,就是因为某种未知的罕见的原因,线程被从等待状态唤醒了,但其实共享变量(即条件)并未变为true。因此此时应继续等待】

std::deque<int> q;
std::mutex mu;
std::condition_variable cond;

void function_1() //生产者

    int count = 10;
    while (count > 0) 
    
        std::unique_lock<std::mutex> locker(mu);
        q.push_front(count);
        locker.unlock();
        cond.notify_one();  // Notify one waiting thread, if there is one.
        std::this_thread::sleep_for(std::chrono::seconds(1));
        count--;
    


void function_2() //消费者

    int data = 0;
    while (data != 1) 
    
        std::unique_lock<std::mutex> locker(mu);
        // while (q.empty())判断是否为错误的的notify
        while (q.empty())
            cond.wait(locker); // Unlock mu and wait to be notified
        data = q.back();
        q.pop_back();
        locker.unlock();
        std::cout << "t2 got a value from t1: " << data << std::endl;
    

int main() 

    std::thread t1(function_1);
    std::thread t2(function_2);
    t1.join();
    t2.join();
    return 0;

核心:

①、在消费者里判断队列是否为空后,如果不为空则wait,等待生产者发送notify信号

②、在生产者那里,如果生产了任务,则发送notify信号,告诉消费者可以试图退出wait,判断队列是否为空,如果有任务则调度处理任务,如果还是空则说明此次notify是错误的,可能是其他地方发出来干扰的,生产者继续wait。

③、流程:

软件开启,生成消费者线程消费队列,应该是一个while循环,在循环里获取锁,再来一个while循环判断条件,如果条件不成立则wait,wait会自动释放锁

此时消费者已经没有锁了,在生产者线程里,获取锁,然后往里面加任务,退出作用域释放锁,然后notify告知消费者退出wait,消费者重新获取锁,然后从队列里取任务;

整个过程,生产者加任务时生产者持有锁,消费者取任务时消费者持有锁。

资料:

C++线程互斥、同步 - 朱小勇 - 博客园

C++条件变量 - 朱小勇 - 博客园

以上是关于C++ std::condition_variable 是什么 有什么用 条件变量 线程同步的主要内容,如果未能解决你的问题,请参考以下文章

C++之父的C++元宇宙

[C++]C++入门到入土篇 HelloWorld 解析 && C++入门

怎么找C++函数需要的头文件?(C++头文件C++函数文档C++文档)

如何识别项目是托管 c++ 项目还是非托管 c++ 项目

C++ 程序员应该使用哪些 C++ 习语? [关闭]

十类C++标准库 十类C++标准库简介