C++11 条件变量

news2025/7/28 14:46:44

目录

条件变量

1 成员函数 

wait函数

wait_for函数

wait_until函数

notify_one函数

notify_all函数

2 Demo1

sync_queue.h

condition-sync-queue.cpp

3 Demo2

2-sync_queue.h

2-condition-sync-queue.cpp


C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

条件变量

        互斥量是多线程间同时访问某一共享变量时,保证变量可被安全访问的手段。但单靠互斥量无法实现线程的同步。

        线程同步是指线程间按照预定的先后次序顺序进行的行为。

      C++11对这种行为也提供了有力的支持,这就是条件变量。条件变量位于头文件condition_variable下。

http://www.cplusplus.com/reference/condition_variable/condition_variable

条件变量使用过程:

1. 拥有条件变量的线程获取互斥量;

2. 循环检查某个条件,如果条件不满足则阻塞直到条件满足;如果条件满足则向下执行;

3. 某个线程满足条件执行完之后调用notify_one或notify_all唤醒一个或者所有等待线程。

条件变量提供了两类操作:wait和notify。这两类操作构成了多线程同步的基础。

1 成员函数 

wait函数

函数原型

void wait (unique_lock<mutex>& lck);

template <class Predicate>

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

包含两种重载,第一种只包含unique_lock对象,另外一个Predicate 对象(等待条件),这里必须使用unique_lock,因为wait函数的工作原理:
1.当前线程调用wait()后将被阻塞并且函数会解锁互斥量,直到另外某个线程调用notify_one或者notify_all唤醒当前线程;一旦当前线程获得通知(notify),wait()函数也是自动调用lock(),同理不能使用lock_guard对象


2.如果wait没有第二个参数,第一次调用默认条件不成立,直接解锁互斥量并阻塞到本行,直到某一个线程调用notify_one或notify_all为止,被唤醒后,wait重新尝试获取互斥量,如果得不到,线程会卡在这里,直到获取到互斥量,然后无条件地继续进行后面的操作。


3.如果wait包含第二个参数,如果第二个参数不满足,那么wait将解锁互斥量并堵塞到本行,直到某一个线程调用notify_one或notify_all为止,被唤醒后,wait重新尝试获取互斥量,如果得不到,线程会卡在这里,直到获取到互斥量,然后继续判断第二个参数,如果表达式为false,wait对互斥量解锁,然后休眠,如果为true,则进行后面的操作。

wait_for函数

函数原型:
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);

        和wait不同的是,wait_for可以执行一个时间段,在线程收到唤醒通知或者时间超时之前,该线程都会处于阻塞状态,如果收到唤醒通知或者时间超时,wait_for返回,剩下操作和wait类似。 

wait_until函数

函数原型:
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);

        与wait_for类似,只是wait_until可以指定一个时间点,在当前线程收到通知或者指定的时间点超时之前,该线程都会处于阻塞状态。如果超时或者收到唤醒通知,wait_until返回,剩下操作和wait类似

notify_one函数

函数原型:
void notify_one() noexcept;

解锁正在等待当前条件的所有线程,如果没有正在等待的线程,则函数不执行任何操作。

notify_all函数

函数原型:
void notify_one() noexcept;

解锁正在等待当前条件的所有线程,如果没有正在等待的线程,则函数不执行任何操作。

 

2 Demo1

使用条件变量实现一个同步队列,同步队列作为一个线程安全的数据共享区,经常用于线程之间数据读取。 

sync_queue.h

#ifndef SYNC_QUEUE_H
#define SYNC_QUEUE_H
#include<list>
#include<mutex>
#include<thread>
#include<condition_variable>
#include <iostream>

template<typename T>
class SyncQueue
{
private:
    bool IsFull() const
    {
        return _queue.size() == _maxSize;
    }

    bool IsEmpty() const
    {
        return _queue.empty();
    }

public:
    SyncQueue(int maxSize) : _maxSize(maxSize)
    {
    }

    void Put(const T& x)
    {
        std::lock_guard<std::mutex> locker(_mutex);

        while (IsFull())
        {
            std::cout << "full wait... size " << _queue.size()  << std::endl;
            _notFull.wait(_mutex);
        }

        _queue.push_back(x);
        _notEmpty.notify_one();
    }

    void Take(T& x)
    {
        std::lock_guard<std::mutex> locker(_mutex);

        while (IsEmpty())
        {
            std::cout << "empty wait.." << std::endl;
            _notEmpty.wait(_mutex);
        }

        x = _queue.front();
        _queue.pop_front();
        _notFull.notify_one();
    }

    bool Empty()
    {
        std::lock_guard<std::mutex> locker(_mutex);
        return _queue.empty();
    }

    bool Full()
    {
        std::lock_guard<std::mutex> locker(_mutex);
        return _queue.size() == _maxSize;
    }

    size_t Size()
    {
        std::lock_guard<std::mutex> locker(_mutex);
        return _queue.size();
    }

    int Count()
    {
        return _queue.size();
    }

private:
    std::list<T> _queue;                  //缓冲区
    std::mutex _mutex;                    //互斥量和条件变量结合起来使用
    std::condition_variable_any _notEmpty;//不为空的条件变量
    std::condition_variable_any _notFull; //没有满的条件变量
    int _maxSize;                         //同步队列最大的size
};
#endif // SYNC_QUEUE_H

condition-sync-queue.cpp

#include <iostream>
#include "3-1-sync_queue.h"

#include <thread>
#include <iostream>
#include <mutex>
using namespace std;
SyncQueue<int> syncQueue(5);

void PutDatas()
{
    for (int i = 0; i < 20; ++i)
    {
        syncQueue.Put(i);
    }
    std::cout << "PutDatas finish\n";
}

void TakeDatas()
{
    int x = 0;

    for (int i = 0; i < 20; ++i)
    {
        syncQueue.Take(x);
        std::cout << x << std::endl;
    }
    std::cout << "TakeDatas finish\n";
}

int main(void)
{
    std::thread t1(PutDatas);  // 生产线程
    std::thread t2(TakeDatas); // 消费线程

    t1.join();
    t2.join();

    std::cout << "main finish\n";
    return 0;
}

代码中用到了std::lock_guard,它利用RAII机制可以保证安全释放mutex

std::lock_guard<std::mutex> locker(_mutex);
while (IsFull())
{
    std::cout << "full wait..." << std::endl;
    _notFull.wait(_mutex);
}

可以改成 

std::lock_guard<std::mutex> locker(_mutex);
_notFull.wait(_mutex, [this] {return !IsFull();});

        两种写法效果是一样的,但是后者更简洁,条件变量会先检查判断式是否满足条件,如果满足条件则重新获取mutex,然后结束wait继续往下执行;如果不满足条件则释放mutex,然后将线程置为waiting状态继续等待。


        这里需要注意的是,wait函数中会释放mutexlock_guard这时还拥有mutex,它只会在出了作用域之后才会释放mutex,所以这时它并不会释放,但执行wait时会提前释放mutex

        从语义上看这里使用lock_guard会产生矛盾,但是实际上并不会出问题,因为wait提前释放锁之后会处于等待状态,在被notify_one或者notify_all唤醒后会先获取mutex这相当于lock_guardmutex在释放之后又获取到了,因此,在出了作用域之后lock_guard自动释放mutex不会有问题。这里应该用unique_lock,因为unique_lock不像lock_guard一样只能在析构时才释放锁,它可以随时释放锁,因此在wait时让unique_lock释放锁从语义上更加准确。

        使用unique_lockcondition_variable_variable改写,改写为用等待一个判断式的方法来实现一个简单的队列。 

 

3 Demo2

2-sync_queue.h

#ifndef SIMPLE_SYNC_QUEUE_H
#define SIMPLE_SYNC_QUEUE_H
#include <thread>
#include <condition_variable>
#include <mutex>
#include <list>
#include <iostream>

template<typename T>
class SimpleSyncQueue
{
public:
    SimpleSyncQueue(){}

    void Put(const T& x)
    {
        std::lock_guard<std::mutex> locker(_mutex);
        _queue.push_back(x);
        _notEmpty.notify_one();
    }

    void Take(T& x)
    {
        std::unique_lock<std::mutex> locker(_mutex);
        _notEmpty.wait(locker, [this]{return !_queue.empty(); });

        x = _queue.front();
        _queue.pop_front();
    }

    bool Empty()
    {
        std::lock_guard<std::mutex> locker(_mutex);
        return _queue.empty();
    }

    size_t Size()
    {
        std::lock_guard<std::mutex> locker(_mutex);
        return _queue.size();
    }

private:
    std::list<T> _queue;
    std::mutex _mutex;
    std::condition_variable _notEmpty;
};
#endif // SIMPLE_SYNC_QUEUE_H

2-condition-sync-queue.cpp

#include <iostream>
#include <thread>
#include <iostream>
#include <mutex>

#include "3-2-sync_queue2.h"

using namespace std;
SimpleSyncQueue<int> syncQueue;

void PutDatas()
{
    for (int i = 0; i < 20; ++i)
    {
        syncQueue.Put(888);
    }
}

void TakeDatas()
{
    int x = 0;

    for (int i = 0; i < 20; ++i)
    {
        syncQueue.Take(x);
        std::cout << x << std::endl;
    }
}

int main(void)
{
    std::thread t1(PutDatas);
    std::thread t2(TakeDatas);

    t1.join();
    t2.join();

     std::cout << "main finish\n";
    return 0;
}

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

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

相关文章

汇编内中断

内中断 文章目录内中断1.内中断的产生2.中断处理程序、中断向量表、中断过程3.iret指令4.除法错误中断的处理5.单步中断6.int指令7.BIOS和DOS所提供的中断例程1.内中断的产生 任何一个通用的CPU&#xff0c;都具备一种能力&#xff0c;可以在执行完当前正在执行的指令之后&…

StartDT奇点云通过CMMI5全球软件领域最高级别成熟度认证

近日&#xff0c;奇点云正式通过全球软件领域最高级别认证——CMMI 5级认证&#xff0c;标志着奇点云的软件技术研发能力、项目管理能力、质量保障能力、方案交付能力等均达到优化管理级的国际先进水平。 CMMI&#xff08;软件能力成熟度模型集成&#xff09;是国际上用于评价软…

JAVA开发(Redis的使用, redis数据类型)

Redis是一个缓存型数据库&#xff0c;或者平时就叫它缓存。它支持存储的类型有以下几种&#xff1a; string&#xff08;字符串&#xff09; hash&#xff08;哈希&#xff09; list&#xff08;列表&#xff09; set&#xff08;集合&#xff09; zset(sorted set&#xff1a;…

智慧电网解决方案-最新全套文件

智慧电网解决方案-最新全套文件一、建设背景二、思路架构三、建设方案四、获取 - 智慧电网全套最新解决方案合集一、建设背景 电力公司如今面临的紧迫需求问题似乎无穷无尽&#xff0c;例如&#xff1a;提高可靠性&#xff1b;降低成本&#xff1b;提高效率&#xff1b;满足环…

(十)C++中的左值lvalue右值rvaue

文章目录1.C中的变量名是如何存储及引用2.C中的左值与右值3.右值引用4.移动语义move函数参考文献欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 1.C中的变量名是如何存储及引用 int a 0;如上&#xff0c;在C中声明一个变量时&#xff0…

【0143】 System V共享内存(Shared Memory)

文章目录 1. 共享内存1.1 共享内存分类2. System V IPC密钥3. System V 共享内存调用3.1 shmget()3.2 shmat()3.3 shmdt()3.4 shmctl()4. 实战演练4.1 服务端程序代码实现4.2 客户端程序代码实现1. 共享内存 共享内存是 Linux 和其他类 Unix 系统下可用的三种进程间通信 (IPC)…

Docker下安装Zookeeper以及Kafka

一、安装Zookeeper 1. 查看Zookeeper镜像以及版本 访问Zookeeper镜像库地址 也可以通过命令docker search zookpper在命令行搜索zookeeper镜像&#xff0c;但是通过该命令无法查看远程镜像版本信息。 NAME&#xff1a;镜像名(镜像仓库源的名称) DESCRIPTION&#xff1a;对该镜…

yolov5剪枝实战2:网络剪枝原理介绍

1. 网络轻量化相关技术 网络轻量化的相关技术分类:1. 网络剪枝(Network pruning) 2. 稀疏表示(Sparse representation) 3. Bits precision(低比特表示,比如不用浮点型,使用int量化) 4. Kownledge distillation(知识蒸馏) 2. 网络剪枝 神经网络一般都是over-parameterized,…

SharePoint Integrator Delphi版

SharePoint Integrator Delphi版 SharePoint Integrator包含易于使用的组件&#xff0c;用于连接流行的SharePoint Server结构&#xff0c;如网站、列表和文档。SharePoint Integrator组件支持对SharePoint对象的访问&#xff0c;允许应用程序轻松地查询和修改列表、访问托管文…

京东发布第三季度财报员工总数近50万 “以实助实”助力高质量就业

11月18日&#xff0c;京东集团&#xff08;纳斯达克股票代码&#xff1a;JD&#xff0c;港交所股票代号&#xff1a;9618&#xff09;发布了2022年三季度业绩。其中净收入为2435亿元人民币&#xff0c;同比增速高于同期国内社会消费品零售总额3.5%的增速&#xff1b;其中&#…

【17-微服务网关之Spring Cloud GatewaySpring Cloud Gateway网关服务搭建】

一.知识回顾 【0.三高商城系统的专题专栏都帮你整理好了&#xff0c;请点击这里&#xff01;】 【1-系统架构演进过程】 【2-微服务系统架构需求】 【3-高性能、高并发、高可用的三高商城系统项目介绍】 【4-Linux云服务器上安装Docker】 【5-Docker安装部署MySQL和Redis服务】…

“知识图谱补全”术语:知识图谱补全、三元组分类、链接预测

基本简介&#xff1a; 知识图谱补全通常定义为“三元组分类”或“链接预测”任务。首先&#xff0c;一条知识在知识图谱中通常由三元组表示&#xff1a;“头实体&#xff0c;关系&#xff0c;尾实体”。三元组分类即对于给定的三元组&#xff0c;预测其正确的概率。而链接预测…

VM虚拟机卡顿、闪退一系列问题与卸载重装问题(详细版)

最新发现好多虚拟机有闪退现象&#xff0c;对此我给出一系列解决方案&#xff0c;仅供参考 一.软件问题 1.虚拟机闪退 首先&#xff0c;如果是VMware Workstation Pro也就是软件本身闪退问题&#xff0c;即还没有运行或打开系统时就已经闪退&#xff0c;说明软件有问题&…

Windows系统VirtualBox下载与安装

Windows系统VirtualBox下载与安装 一、下载&#xff1a;https://www.virtualbox.org/wiki/Downloads 1.安装包 2.扩展包&#xff08;对USB 2.0、USB 3.0、远程桌面协议 VRDP等实用功能的支持&#xff09; 二、安装 1.如果安装出现本机缺少必要包的情况&#xff0c;不要慌&am…

3.1版本【HarmonyOS 第一课】正式上线!参与学习赢官方好礼>>

【课程介绍】《HarmonyOS第一课》是跟随版本迭代不断推出的系列化课程&#xff0c;本期课程基于HarmonyOS 3.1版本的新技术和特性&#xff0c;每个课程单元里面都包含视频、Codelab、文章和习题&#xff0c;帮助您快速掌握HarmonyOS的应用开发&#xff0c;快速了解新的特性和技…

基于PHP+MySQL汽车展览会网站的设计与实现

随着我国经济的发展,汽车已经进入了家家户户,但是很多时候人们因为各种原因需要进行汽车出行,但是很多时候人们有不知道购买什么样的汽车更适合自己,如果挨个去汽车4S既浪费时间又不能够把各类汽车都看全,人们更希望通过发达的车展来查询自己所需的汽车信息,本系统也是出于这样…

分享几个常用的可以从外部攻击视角发现甲方公司安全问题的开源工具

资产管理平台-ARL https://github.com/TophantTechnology/ARL 作为甲方&#xff0c;一定要有自己的资产平台&#xff0c;主要目的就是基于外部攻击视角不断发现风险&#xff0c;当然放在内网也可以&#xff0c;测试了很多开源的工具&#xff0c;最终还是选择了ARL&#xff0c;…

【深入理解C++】左值引用、常引用、右值引用、std::move()函数

文章目录1.左值引用2.常引用2.1 const引用可以指向临时数据2.2 const引用可以指向不同类型的数据2.3 const引用作为函数参数3.右值引用4.std::move()函数1.左值引用 左值引用只能绑定到左值上&#xff0c;不能绑定到右值上。 左值引用不能绑定到临时变量上&#xff0c;因为临…

是德N5290A矢量网络分析仪技术参数及操作步骤

网络分析仪能对被测量器件(主要分为有源元件和无源元件)的线性和非线性特性(幅频特性&#xff0c;相频特性&#xff0c;时频特性&#xff0c;功率频率特性等)进行表征。 那么大家真的了解网络分析仪吗?下面西安安泰测试以是德科技(安捷伦)E50系列矢量网络分析仪为例介绍仪器基…

基于PHP+MySQL汽车租赁管理系统的设计与实现

虽然汽车已经进入了家家户户,但是很多时候人们因为各种原因需要进行汽车的租赁,可能是到达的城市比较远,也可能是经济实力不允许,或者是对车辆的要求不高,暂时没必要买车等,这些特殊的原因导致了汽车租赁行业的兴起。越来越多的用车用户希望通过租赁来直接获取使用的使用权,这样…