14 C++11线程同步之条件变量

news2025/1/15 16:54:51

在学习条件变量之前需要先了解下std::unique_lock;条件变量 condition_variable需要配合std::unique_lock使用;

std::unique_lock

std::unique_lock的详细细节参考此篇文章。

C11条件变量

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

  • condition_variable:需要配合std::unique_lockstd::mutex进行wait操作,也就是阻塞线程操作;
  • condition_variable_any:可以和任意带有lock()、unlock()语意的mutex搭配使用:
    • std::mutex:独占的非递归互斥锁
    • std::timed_mutex:带超时的独占非递归互斥锁
    • std::recursive_mutex:不带超时功能的递归互斥锁
    • std::recursive_timed_mutex:带超时的递归互斥锁

1. condition_variable

1.1 成员函数

condition_variable的成员函数主要分为两部分:线程阻塞函数和线程通知函数,这些函数定于头文件<condition_variable>。

// ①
void wait (unique_lock<mutex>& lck);
// ②
template <class Predicate>
void wait (unique_lock<mutex>& lck, Predicate pred);
//③
template <class Rep, class Period>
cv_status wait_for (unique_lock<mutex>& lck,
                    const chrono::duration<Rep,Period>& rel_time);
//④
template <class Rep, class Period, class Predicate>
bool wait_for(unique_lock<mutex>& lck,
               const chrono::duration<Rep,Period>& rel_time, Predicate pred);
//⑤
template <class Clock, class Duration>
cv_status wait_until (unique_lock<mutex>& lck,
                      const chrono::time_point<Clock,Duration>& abs_time);
//⑥
template <class Clock, class Duration, class Predicate>
bool wait_until (unique_lock<mutex>& lck,
                 const chrono::time_point<Clock,Duration>& abs_time, Predicate pred);
//⑦
void notify_one() noexcept;
//⑧
void notify_all() noexcept;                 

函数①:调用该函数的线程直接被阻塞

函数②:该函数的第二个参数是一个判断条件,是一个返回值为布尔类型的函数

该参数可以传递一个有名函数的地址,也可以直接指定一个匿名函数
表达式返回false当前线程被阻塞,表达式返回true当前线程不会被阻塞,继续向下执行

函数③、④:
wait_for() 函数和 wait() 的功能是一样的,只不过多了一个阻塞时长,假设阻塞的线程没有被其他线程唤醒,当阻塞时长用完之后,线程就会自动解除阻塞,继续向下执行。
函数⑤、⑥:
wait_until() 函数和 wait_for() 的功能是一样的,它是指定让线程阻塞到某一个时间点,假设阻塞的线程没有被其他线程唤醒,当到达指定的时间点之后,线程就会自动解除阻塞,继续向下执行。

如果线程被该函数阻塞,这个线程会释放占有的互斥锁的所有权,当阻塞解除之后这个线程会重新得到互斥锁的所有权,继续向下执行(这个过程是在函数内部完成的,了解这个过程即可,其目的是为了避免线程的死锁)

函数⑦、⑧
notify_one():唤醒一个被当前条件变量阻塞的线程
notify_all():唤醒全部被当前条件变量阻塞的线程

eg:

#include <iostream>
#include <thread>
#include <mutex>
#include <list>
#include <functional>
#include <condition_variable>

using namespace std;

class SyncQueue
{
public:
    SyncQueue(int maxSize) :m_maxSize(maxSize) {}
    void put(const int& x)
    {
        unique_lock<mutex> locker(m_mutex);
        
        //while (m_queue.size() == m_maxSize)
        //{
        //    cout << "任务队列已满,请耐心等待..." << endl;
        //    m_notFull.wait(locker);
        //}

        m_notFull.wait(locker, [this]() { return m_maxSize != m_queue.size();});
        m_queue.push_back(x);
        cout << x << "被生产" << endl;
        m_notEmpty.notify_one();
    }

    int take()
    {
        unique_lock<mutex> locker(m_mutex);

        //while (m_queue.empty())
        //{
        //    cout << "任务队列已空,请耐心等待..." << endl;
        //    m_notEmpty.wait(locker);
        //}

        m_notEmpty.wait(locker, [this]() { return m_maxSize != m_queue.size(); });
        int x = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();
        cout << x << "被消费" << endl;
        return x;
    }

private:
    list<int> m_queue;
    mutex m_mutex;
    condition_variable m_notEmpty;
    condition_variable m_notFull;
    int m_maxSize;

};

int main()
{
    SyncQueue taskQ(50);
    auto produce = bind(&SyncQueue::put, &taskQ, placeholders::_1);
    auto consume = bind(&SyncQueue::take, &taskQ);
    thread t1[3];
    thread t2[3];
    for (int i = 0; i < 3; i++)
    {
        t1[i] = thread(produce, i + 100);
        t2[i] = thread(consume);
    }

    for (int i = 0; i < 3; ++i)
    {
        t1[i].join();
        t2[i].join();
    }
    system("pause");
    return 0;
}   

在这里插入图片描述

2. condition_variable_any

condition_variable_any 用法与condition_variable类似,区别如下:
condition_variable 配合 unique_lock 使用更灵活一些,可以在在任何时候自由地释放互斥锁,而 condition_variable_any 如果和 lock_guard 一起使用必须要等到其生命周期结束才能将互斥锁释放。但是,condition_variable_any 可以和多种互斥锁配合使用,应用场景也更广,而 condition_variable 只能和独占的非递归互斥锁(mutex)配合使用,有一定的局限性。

具体用法参考该文章

3. 案例

看下LeetCode上一个题,用条件变量来解:
给你一个类:
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/24289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第04章_运算符

第04章_运算符 1. 算术运算符 算术运算符主要用于数学运算&#xff0c;其可以连接运算符前后的两个数值或表达式&#xff0c;对数值或表达式进行加&#xff08;&#xff09;、减&#xff08;-&#xff09;、乘&#xff08;*&#xff09;、除&#xff08;/&#xff09;和取模&…

使用dbeaver连接GaussDB数据库(集中式)

服务端方式登录 默认初始用户登录方式&#xff1a; [ommgaussdb01 ~]$ gsql -d postgres -p 30100 gsql ((GaussDB Kernel V500R002C10 build 04860477) compiled at 2022-10-28 20:04:35 commit 3892 last mr 8894 release) Non-SSL connection (SSL connection is recommen…

XAML标记扩展(3)

一、RelativeSource属性 我们进行Bingding时&#xff0c;如果明确知道数据源的Name&#xff0c;就能用Source或者ElementName进行绑定&#xff0c;但是有时候我们需要绑定的数据源可能没有明确的Name&#xff0c;此时我们就需要利 用Bingding的RelativeSource进行绑定&#xf…

虚拟数字人/直播/捏脸/3D/metahuman 实时人脸动作捕捉 开发笔记

拍照生成数字人 流程 手机&#xff08;iphone xr以上&#xff09;拍照&#xff08;脸部&#xff09;&#xff0c;导入到unrealmetahuman做数字人 【中文】从0开始捏一个自己的虚拟人&#xff0c;手机扫描到MetaHuman做一个自己的虚拟人_哔哩哔哩_bilibili 涉及APP iphone …

[附源码]java毕业设计校园兼职招聘系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

YUV图像基础知识

概念 YUV和RGB的功能类似&#xff0c;都是用来表示图像色彩的。但是对于 YUV 所表示的图像&#xff0c;Y 和 UV 分量是分离的。如果只有 Y 分量而没有 UV 分离&#xff0c;那么图像表示的就是黑白图像。彩色电视机采用的就是 YUV 图像&#xff0c;解决与和黑白电视机的兼容问题…

swift枚举(二)

swift枚举(一) No-payload enums 布局比较简单&#xff0c;也好理解&#xff0c;接下来看看 Single-payload enums Single-payload enums enum IFLEnum {case test_one(Bool)case test_twocase test_threecase test_four}print(MemoryLayout<IFLEnum>.size)print(Memor…

Vue事件处理器:事件绑定基础、事件修饰符:stop、prevent、capture、self、once;

先看代码&#xff1a; <body><div id"box">{{count}}<button click"handleAdd1()">add1</button><button click"handleAdd2">add2</button></div><script>new Vue({el: "#box",dat…

关于电脑使用的实用技巧

电脑几乎是我们每天都会用到的工具&#xff0c;那么电脑的使用技巧你知道多少呢&#xff1f;今天&#xff0c;我来为大家整理了几个常用的技巧&#xff0c;希望对大家的工作或学习效率有所帮助。 技巧一&#xff1a;快速查找文档按Windows E键打开电脑中的资源管理器&#xff0…

[附源码]SSM计算机毕业设计个性化新闻推荐系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

记一次神奇的 pipe 错误

文章目录1. 写在最前面2. 问题原因3. 解决问题3.1 CAP 的历史说明3.2 CAP 拆分的能力集合说明3.3 如何知道某个程序的能力集合3.3.1 查看只能写入 4096B 大小的程序能力位图3.3.2 查看能写入 65536B 大小的能力位图3.3.3 比较两个能力位图3.3.4 为 pod 增加 CAP_SYS_RESOURCE 的…

数字信号处理-4-三角函数合成与傅里叶级数

1 三角函数合成 函数正交&#xff08;数字信号处理-3-函数的正交&#xff09;&#xff0c;那它们相互之间无法通过组合得出对方的表达式&#xff0c;如&#xff1a;sinx 与 cosx 正交&#xff0c;acosnx 是无法表示 sinx 的。相互正交的各类三角函数是制作许多波形的基本单位。…

KT148A语音芯片按键版本一对一触发播放功能描述_V4

目录 一、简介 KT148A语音芯片--按键版本&#xff0c;支持3个IO口一对一触发 。同时也支持用户自己更换芯片内部的声音文件&#xff0c;方法&#xff0c;参考我们另外一份文档的描述“20220704_KT148A芯片自己更换声音的方法V3”。请留意&#xff0c;需要样品联系客服&#xf…

钡铼EdgeIO边缘计算 I/O 控制器

BL200 系列耦合器是一个数据采集和控制系统&#xff0c;基于强大的 32 位微处理器设计&#xff0c; 采用 Linux 操作系统&#xff0c;支持 Modbus&#xff0c;MQTT&#xff0c;OPC UA&#xff0c;Profinet&#xff0c;EtherCAT&#xff0c;Ethernet/IP&#xff0c; BACnet/IP…

程序员月薪8000,丢人吗?

近日&#xff0c;拉勾招聘数据研究院针对平台的程序员群体开展了深度调研&#xff0c;发布了《拉勾招聘2022程序员群体职场洞察报告》&#xff0c;呈现程序员最新的职场薪资水平。 *数据来源拉勾 从上图中可以看到&#xff0c;74%的00后应届毕业生的月薪在1-3万元区间&#…

C++语言的return语句的说明

C语言的return语句的说明 为了完成某一功能的程序指令&#xff08;语句&#xff09;的集合&#xff0c;称为函数。在程序中&#xff0c;编写函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务来解决&#xff0c;而且&#xff0c;同一个任务&#xff08;函…

Qt5开发从入门到精通——第十一篇三节(Qt5 事件处理及实例——事件过滤及实例)

提示&#xff1a;欢迎小伙伴的点评✨✨&#xff0c;相互学习c/c应用开发。&#x1f373;&#x1f373;&#x1f373; 博主&#x1f9d1;&#x1f9d1; 本着开源的精神交流Qt开发的经验、将持续更新续章&#xff0c;为社区贡献博主自身的开源精神&#x1f469;‍&#x1f680; 文…

WPF XAML介绍

一、XAML 简介 XAML是一种声明性标记语言&#xff0c;它简化了为.NET Framework应用程序创建UI的过程。在声明性XAML标记中创建可见的UI元素&#xff0c;可以折叠隐藏代码&#xff0c;使程序界面编程更加简单和简洁。 XAML直接以程序集中定义的一组特定后备类型表示对象的实例化…

Oracle Primavera Unifier组合管理器(Portfolio Manager)

目录 一&#xff1a;前言 二&#xff1a;介绍​​​​​​​ 一&#xff1a;前言 Oracle Primavera Unifier uDesigner Portfolio Manager 项目组合管理器是公司的预算员/计划工程师可以收集项目&#xff08;计划和执行中&#xff09;的成本和进度信息&#xff0c;并对“假设…

Webpack 5 超详细解读(六)

51.TerserPlugin 压缩 JS webapck中提供了压缩 js 代码的方式,可以移除无用代码、替换变量名等,减少编译后文件体积,提升加载速度。 不同mode 在 webpack配置文件 webpack.config.js中通过将 mode设置为 development或者 production,会对代码进行不同的处理。 可以发现,…