首页 > 编程语言 >C++11标准库 条件变量 <condition_variable> 梳理

C++11标准库 条件变量 <condition_variable> 梳理

时间:2024-07-15 10:09:42浏览次数:16  
标签:11 std 变量 C++ 线程 variable 梳理 condition wait

目录

<condition_variable>

条件变量是C++11提供的另外一种用于等待的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时时,才会唤醒当前阻塞的线程。条件变量需要和互斥量配合起来使用,C++11提供了两种条件变量:

条件变量为什么叫变量?

在计算机科学和并发编程中,条件变量是一种用于线程同步的机制,它们之所以被称为“变量”,主要有以下几个原因:

  1. 状态表示:条件变量本质上是一个表示状态的对象,这个状态可以被其他线程检查和修改。变量这个词意味着它是一个可以存储和表示某种状态的实体。
  2. 动态性:条件变量的状态是动态变化的。线程可以等待一个条件变量的某个状态,然后在条件满足时被唤醒。这种动态变化的特性使得它像一个普通的变量,可以在程序运行时不断变化。
  3. 操作性:条件变量可以通过特定的操作来改变其状态。通常,条件变量有两个主要操作:等待(wait)和通知(signal或broadcast)。这些操作类似于对普通变量进行的读写操作,只不过这些操作影响的是线程的执行流。
  4. 命名约定:在许多编程语言和库中,条件变量被设计为一种数据结构或对象,并且通常以变量的形式存在于代码中。为了与其他同步机制(如互斥锁、信号量等)区分开来,并保持命名的一致性,使用“变量”这个词来描述它们。

综上所述,条件变量被称为“变量”主要是因为它们具有状态表示的特性,可以通过操作改变状态,并在程序中以变量的形式出现,从而帮助线程实现同步。

condition_variable类

condition_variable实现在<condition_variable>中,在VS2019中内有声明,但是GCC没有.

类方法

  1. 线程等待(阻塞/休眠)函数
1. void wait (unique_lock<mutex>& lck); //,解锁,进入休眠,等待唤醒
2. template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);

如果线程被该函数阻塞,这个线程会释放占有的互斥锁的所有权,当阻塞解除之后这个线程会重新得到互斥锁的所有权,继续向下执行

  • condition_variable的wait()的lck参数无法直接使用互斥锁,必须搭配std::unique_lock<>类使用

  • wait的第二重载方法中,Pred参数是一个模板参数,用于接收返回值为bool函数或函数对象/lambda表达式.

    每次唤醒在wait队列内休眠的线程时,线程都会检查Rred的值,只有为真时才会继续往下执行,否则继续休眠

    • Pred值为假,线程进入休眠,等待唤醒
    • Pred值为真,线程继续向下执行.
  1. 线程通知/唤醒函数 -- (notify:通知)
1. void notify_one() noexcept; //在wait队列中唤醒一个
2. void notify_all() noexcept; //全部唤醒
  1. wait_for和wait_until
    wait_for()函数,waitr_until()函数都和wait()的功能是一样的,只不过多了一个阻塞时长,假设阻塞的线程没有被其他线程唤醒,当阻塞时长用完之后,线程就会自动解除阻塞,继续向下执行。
wait_for
a.
	template <class Clock, class Duration>
	cv_status wait_until (unique_lock<mutex>& lck,
	                      const chrono::time_point<Clock,Duration>& abs_time);
b.
	template <class Clock, class Duration, class Predicate>
	bool wait_until (unique_lock<mutex>& lck,
	                 const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);
                 
wait_until
a.
	template <class Clock, class Duration>
	cv_status wait_until (unique_lock<mutex>& lck,
	                      const chrono::time_point<Clock,Duration>& abs_time);
b.
	template <class Clock, class Duration, class Predicate>
	bool wait_until (unique_lock<mutex>& lck,
	                 const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);

生产者消费者模型 -- 阻塞队列

单条件变量版

运用:wait(lck), wait(lck,Pred), notify_all(), condition_variable

#include<iostream> //std::cout 
#include<thread> //std::thread
#include<mutex> //std::mutex, std::unique_lock, std::lock_guard
#include<queue> //std::queue
#include<condition_variable> //std::condition_variable
#include<functional> //std::bind

//设计概要
/*
定长队列+自动管理增删
同步 == 独占互斥锁
- 生产者生产 == 增加 -- bool?生产成功返回true,放不下false --- (生产者一定知道自己已生产)生产:通知消费者
- 消费者消费 == 删除 -- bool?消费成功返回true,没有了false --- (同理)       消费:通知生产者
*/

//单条件变量版 --  简化逻辑
template<class T>
class  BlockQueue {

public:
    bool isEmpty() {
        return _bqueue.empty();
    }
    bool isFull() {
        return _bqueue.size() == _capacity;
    }
    //AddTask
    void Push(const T& t) {
        std::unique_lock<std::mutex> lck(_mtx);
        while (isFull()) {
            cv.wait(lck);
        }
        _bqueue.push(t);
        std::cout << "模拟插入数据..." << t << "\n";
        lck.unlock();
        cv.notify_all();
    }
    //DelTask
    void Pop() {
        std::unique_lock<std::mutex>lck(_mtx);
        cv.wait(lck, [this]() {
            bool flag = isEmpty(); //为空时休眠
            return !flag; //增加可读性的策略
            });
        T t = _bqueue.front();
        _bqueue.pop();
        std::cout << "模拟处理数据..." << t << "\n";
        lck.unlock();
        cv.notify_all();
    }

private:
    std::queue<T> _bqueue;
    std::mutex _mtx;
    std::condition_variable cv;  //必须搭配all
    size_t _capacity = 5; //队列定长大小
    
    //std::condition_variable _not_full;  //非满时唤醒生产者
    //std::condition_variable _not_empty; //非空时唤醒消费者
};


int main() {
    BlockQueue<int> tq;
    auto produce = std::bind(&BlockQueue<int>::Push, &tq, std::placeholders::_1);
    auto consume = std::bind(&BlockQueue<int>::Pop, &tq);
    std::thread t1[5];
    std::thread t2[5];
    for (int i = 0; i < 5; i++) {
        t1[i] = std::thread(produce, i);
        t2[i] = std::thread(consume);
    }

    for (int i = 0; i < 5; i++) {
        t1[i].join();
        t2[i].join();
    }

    return 0;
}

condition_variable_any模板类

condition_variable_any实现在<condition_variable>中,在VS2019中内有声明,但是GCC没有.

区别

condition_variable_any与condition_variable的区别是

  • condition_variable_any可以直接给wait()函数传互斥锁std::mutex、std::timed_mutex、std::recursive_mutex、std::recursive_timed_mutex四种.
  • condition_variable在wait()时必须搭配unique_lock使用

优缺点

  • condition_variable实现简单,效率更高.缺点是只能搭配std::mutex和unique_lock使用
  • condition_variable_any更加灵活,但是实现复杂,效率会低一些,仅仅是低一些,对于现代计算机,这点效率损失不成问题.

标签:11,std,变量,C++,线程,variable,梳理,condition,wait
From: https://www.cnblogs.com/DSCL-ing/p/18302142

相关文章

  • 【C++BFS算法】752 打开转盘锁
    本文涉及知识点C++BFS算法LeetCode752打开转盘锁你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字:‘0’,‘1’,‘2’,‘3’,‘4’,‘5’,‘6’,‘7’,‘8’,‘9’。每个拨轮可以自由旋转:例如把‘9’变为‘0’,‘0’变为‘9’。每次旋转都只能旋......
  • 【NOI】C++数据结构入门之一维数组(一)数组基础
    文章目录前言一、概念1.导入2.数组2.1数组的创建2.2数组的使用二、例题讲解问题:1423-考试成绩的简单统计问题:1153-查找“支撑数”问题:1156-排除异形基因问题:1155-找找谁的身高超过全家的平均身高问题:1231-考试成绩的分布情况三、总结四、感谢前言在......
  • C++11标准库 互斥锁 <mutex> 梳理
    目录<mutex>std::call_once函数例程:使用call_once实现的单例模式std::mutex类--独占互斥锁成员函数std::recursive_mutex类--递归互斥锁使用注意:描述:std::timed_mutex类--超时互斥锁描述:成员函数:std::recursive_timed_mutex类std::lock_guard模板类函数原型:std::uniqu......
  • Win11系统提示找不到LcRes.dll文件的解决办法
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个LcRes.dll文件(挑选合适的版本文件)把它放入......
  • Win11系统提示找不到libGLESv2.dll文件的解决办法
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个libGLESv2.dll文件(挑选合适的版本文件)把它......
  • Win11系统提示找不到learning_tools.dll文件的解决办法
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个learning_tools.dll文件(挑选合适的版本文件......
  • Win11系统提示找不到LockSearchAPI.dll文件的解决办法
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个LockSearchAPI.dll文件(挑选合适的版本文件)......
  • day11| 150. 逆波兰表达式求值 239. 滑动窗口最大值 347.前 K 个高频元素
    代码随想录算法训练营第十一天|150.逆波兰表达式求值239.滑动窗口最大值347.前K个高频元素Leetcode150.逆波兰表达式求值题目链接:https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/题目描述:给你一个字符串数组tokens,表示一个根......
  • 【C++11常见新特性(三)】线程库
    文章目录thread类线程函数参数并行与并发的区别原子性操作库关于atomic类模板对比锁和原子操作lock_guard与unique_locklock_guardunique_lock两个线程交替打印奇数和偶数thread类C++11新特性支持线程,使得C++在并行编程中不需要使用第三方库,并且在原子操作中还引......
  • 基于java+springboot+vue实现的健身房管理系统(文末源码+Lw)113
     基于SpringBoot+Vue的实现的健身房管理系统(源码+数据库+万字Lun文+流程图+ER图+结构图+演示视频+软件包)系统功能:本健身房管理系统管理员,会员,员工。管理员功能有个人中心,会员管理,员工管理,会员卡管理,会员卡类型管理,教练信息管理,解聘管理,健身项目管理,指导项目管理,健身器材管......