C 条件变量使用详解

condition_variable介绍

在C 11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。

其主要成员函数如下:

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

  • 一个线程因等待"条件变量的条件成立"而挂起;

  • 另外一个线程使"条件成立",给出信号,从而唤醒被等待的线程。

为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起;通常情况下这个锁是std::mutex,并且管理这个锁 只能是 std::unique_lockstd::mutex RAII模板类。

上面提到的两个步骤,分别是使用以下两个方法实现:

  • 等待条件成立使用的是condition_variable类成员wait 、wait_for 或 wait_until。

  • 给出信号使用的是condition_variable类成员notify_one或者notify_all函数。

细节说明

在条件变量中只能使用std::unique_lock< std::mutex >说明

unique_lock和lock_guard都是管理锁的辅助类工具,都是RAII风格;它们是在定义时获得锁,在析构时释放锁。它们的主要区别在于unique_lock锁机制更加灵活,可以再需要的时候进行lock或者unlock调用,不非得是析构或者构造时。它们的区别可以通过成员函数就可以一目了然。在这里插入图片描述

wait/wait_for说明

线程的阻塞是通过成员函数wait()/wait_for()/wait_until()函数实现的。这里主要说明前面两个函数:

wait()成员函数

函数声明如下:

void wait( std::unique_lock<std::mutex>& lock );
//Predicate 谓词函数,可以普通函数或者lambda表达式
template< class Predicate >
void wait( std::unique_lock<std::mutex>& lock, Predicate pred );

wait 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,可选地循环直至满足某谓词。

wait_for()成员函数

函数声明如下:

template< class Rep, class Period >
std::cv_status wait_for( std::unique_lock<std::mutex>& lock,const std::chrono::duration& rel_time);
template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lock<std::mutex>& lock,const std::chrono::duration& rel_time,Predicate pred);

wait_for 导致当前线程阻塞直至条件变量被通知,或虚假唤醒发生,或者超时返回。

返回值说明:

  1. 若经过 rel_time 所指定的关联时限则为 std::cv_status::timeout ,否则为 std::cv_status::no_timeout 。

  2. 若经过 rel_time 时限后谓词 pred 仍求值为 false 则为 false ,否则为 true 。

以上两个类型的wait函数都在会阻塞时,自动释放锁权限,即调用unique_lock的成员函数unlock(),以便其他线程能有机会获得锁。这就是条件变量只能和unique_lock一起使用的原因,否则当前线程一直占有锁,线程被阻塞。

notify_all/notify_one

notify函数声明如下:

void notify_one() noexcept;

若任何线程在 *this 上等待,则调用 notify_one 会解阻塞(唤醒)等待线程之一。

void notify_all() noexcept;

若任何线程在 *this 上等待,则解阻塞(唤醒)全部等待线程。

虚假唤醒

在正常情况下,wait类型函数返回时要不是因为被唤醒,要不是因为超时才返回,但是在实际中发现,因此操作系统的原因,wait类型在不满足条件时,它也会返回,这就导致了虚假唤醒。因此,我们一般都是使用带有谓词参数的wait函数,因为这种(xxx, Predicate pred )类型的函数等价于:

while (!pred()) //while循环,解决了虚假唤醒的问题
{wait(lock);
}

原因说明如下:

假设系统不存在虚假唤醒的时,代码形式如下:

if (不满足xxx条件)
{//没有虚假唤醒,wait函数可以一直等待,直到被唤醒或者超时,没有问题。//但实际中却存在虚假唤醒,导致假设不成立,wait不会继续等待,跳出if语句,//提前执行其他代码,流程异常wait();  
}//其他代码
...

正确的使用方式,使用while语句解决:

while (!(xxx条件) )
{//虚假唤醒发生,由于while循环,再次检查条件是否满足,//否则继续等待,解决虚假唤醒wait();  
}
//其他代码
....

条件变量使用

在这里,我们使用条件变量,解决生产者-消费者问题,该问题主要描述如下:

生产者-消费者问题,也称有限缓冲问题,是一个多进程/线程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个进程/线程——即所谓的“生产者”和“消费者”,在实际运行时会发生的问题。

生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。

同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。

生产者-消费者代码如下:

std::mutex g_cvMutex;
std::condition_variable g_cv;//缓存区
std::deque<int> g_data_deque;
//缓存区最大数目
const int  MAX_NUM = 30;
//数据
int g_next_index = 0;//生产者,消费者线程个数
const int PRODUCER_THREAD_NUM  = 3;
const int CONSUMER_THREAD_NUM = 3;void  producer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(500));//加锁std::unique_lock <std::mutex> lk(g_cvMutex);//当队列未满时,继续添加数据g_cv.wait(lk, [](){ return g_data_deque.size() <= MAX_NUM; });g_next_index  ;g_data_deque.push_back(g_next_index);std::cout << "producer_thread: " << thread_id << " producer data: " << g_next_index;std::cout << " queue size: " << g_data_deque.size() << std::endl;//唤醒其他线程 g_cv.notify_all();//自动释放锁}
}void  consumer_thread(int thread_id)
{while (true){std::this_thread::sleep_for(std::chrono::milliseconds(550));//加锁std::unique_lock <std::mutex> lk(g_cvMutex);//检测条件是否达成g_cv.wait( lk,   []{ return !g_data_deque.empty(); });//互斥操作,消息数据int data = g_data_deque.front();g_data_deque.pop_front();std::cout << "\tconsumer_thread: " << thread_id << " consumer data: ";std::cout << data << " deque size: " << g_data_deque.size() << std::endl;//唤醒其他线程g_cv.notify_all();//自动释放锁}
}int main()

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

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

相关文章

HTML5新布局元素布局,HTML5新的布局元素

HTML5相对于HTML4新增了一些布局元素新增布局标签的优点&#xff1a;⒈更加注重文档的结构内容而不是以什么形式展现出来⒉对人的友好&#xff0c;更加语义化&#xff0c;增加代码的可读性⒊对计算机友好&#xff0c;浏览器更容易解析新增布局标签的内容&#xff1a;新增的布局…

pannel添加的子窗体很大_超简单的地瓜粉焖子做法全解,零失败

总以为做焖子是一项很大的工程&#xff0c;如果朋友知道你会做焖子定会用惊叹的语气崇拜你&#xff0c;想多一项厨艺吗&#xff0c;想让家人吃上自己亲手做的焖子吗&#xff1f;使用艺琳农场的地瓜粉一定让你出手不凡的&#xff0c;超简单&#xff0c;往下看哦新手为了担心霍霍…

swagger api文档_带有Swagger的Spring Rest API –创建文档

swagger api文档使REST API易于使用的真正关键是好的文档。 但是&#xff0c;即使您的文档做得很好&#xff0c;您也需要设置公司流程的权利以正确&#xff0c;及时地发布它。 确保利益相关者按时收到是一回事&#xff0c;但是您也要负责API和文档中的更新。 自动完成此过程可轻…

多线程队列的算法优化

【导读】&#xff1a;本文主要讲解多线程队列的优化。多线程队列&#xff08;Concurrent Queue&#xff09;的使用场合非常多&#xff0c;高性能服务器中的消息队列&#xff0c;并行算法中的Work Stealing等都离不开它。对于一个队列来说有两个最主要的动作&#xff1a;添加&am…

购买台式计算机方案,购买电脑的注意事项有哪些?

随着电脑的应用与普及&#xff0c;电脑已经逐渐成为人们学习、工作、生活中不可缺少的工具。同时&#xff0c;电脑的价格在逐渐下降&#xff0c;很多用户开始准备选购自己的电脑。选购电脑要考虑用户的需求、价格承受能力、商家服务质量等。1、明确用户需求购买电脑之前&#x…

linux 文件大小_整理 | Linux下列出目录内容命令

IT服务圈儿有温度、有态度的IT自媒体平台来源&#xff1a;良许Linux(ID&#xff1a;liangxuxiansheng)在 Linux 中&#xff0c;有非常多的命令可以让我们用来执行各种各样的任务。当我们想要像使用文件浏览器一样列出一个目录下的内容时&#xff0c;大家第一时间想到的是 ls 命…

mysql caching_Spring Caching抽象和Google Guava Cache

mysql cachingSpring为缓存昂贵的方法调用提供了强大的现成支持。 这里详细介绍了缓存抽象。 我的目标是使用Spring Guava Cache涵盖Spring现在提供的4.0版本的较新的缓存实现之一。 简而言之&#xff0c;请考虑一种具有几种慢速方法的服务&#xff1a; public class DummyB…

多线程程序中操作的原子性

0. 背景原子操作就是不可再分的操作。在多线程程序中原子操作是一个非常重要的概念&#xff0c;它常常用来实现一些同步机制&#xff0c;同时也是一些常见的多线程Bug的源头。本文主要讨论了三个问题&#xff1a;1. 多线程程序中对变量的读写操作是否是原子的&#xff1f;2. 多…

2018秋计算机基础在线作业华师,18秋华师《计算机基础》在线作业3(标准答案).doc...

【奥鹏】[华中师范大学]华师《计算机基础》在线作业试卷总分:100 得分:100第1题,控制面板可实现__________。A、对计算机全面控制操作B、对硬件驱动、软件设置及Windows外观设置C、计算机的关闭操作D、删除计算机中的任意文件正确答案:B第2题,页眉和页脚的建立方法相似&#xf…

.net mvc actionresult 返回字符串_ASP.NET Core中的Action的返回值类型

在Asp.net Core之前所有的Action返回值都是ActionResult&#xff0c;Json(),File()等方法返回的都是ActionResult的子类。并且Core把MVC跟WebApi合并之后Action的返回值体系也有了很大的变化。ActionResult类ActionResult类是最常用的返回值类型。基本沿用了之前Asp.net MVC的那…

.jdeveloper_在JDeveloper 12.1.3中为WebSocket使用Java API

.jdeveloper介绍 最新版本的JDeveloper 12c&#xff08;12.1.3.0&#xff09;和WebLogic Server 12.1.3一起提供了一些新的Java EE 7功能。 其中之一是对用于WebSocket的JSR 356 Java API的支持。 实际上&#xff0c;从12.1.2.0版本开始就支持WebSocket协议&#xff08;RFC 645…

为什么程序员需要关心顺序一致性,而不是 Cache 一致性?

本文所讨论的计算机模型是Shared Memory Multiprocessor&#xff0c;即我们现在常见的共享内存的多核CPU。本文适合的对象是想用C 或者Java进行多线程编程的程序员。本文主要包括对Sequential Consistency和Cache Coherence的概念性介绍并给出了一些相关例子&#xff0c;目的是…

南科大计算机科学与技术专业如何,广州大学、深圳大学、汕头大学、南方科技大学,如何排名?...

广州大学、深圳大学、汕头大学和南方科技大学都是广东省内的一流大学。为了方便各位广东考生在填报志愿的时候有一个更好的了解&#xff0c;顺哥收集整理了这4所学校的一些信息。希望能帮助到大家。深圳大学2021年校友会排名省内第4&#xff0c;全国第57&#xff0c;中国一流大…

canoco5冗余分析步骤_打造高性能的大数据分析平台

大数据时代&#xff0c;大数据的应用与挖掘&#xff0c;大数据的分析和决策&#xff0c;大数据在经济社会的运行轨道上发挥着愈来愈重要的作用。对于大数据分析&#xff0c;现在好多互联网金融公司和传统的商业银行、证券基金公司都非常看重。个个都想在大数据分析中获得重要信…

C 迭代器iterator的实现原理

在经典的设计模式中&#xff0c;有一种迭代器模式&#xff0c;定义为&#xff1a;提供一个对象来顺序访问聚合对象中的一系列数据&#xff0c;而不暴露聚合对象的内部表示。迭代器的主要优点如下&#xff1a;访问一个聚合对象的内容而无须暴露它的内部表示。遍历任务交由迭代器…

html如何在画布上加层,在Canvas中嵌套Html

大概是这样的&#xff0c;现在需要根据一下上传的图片以及一些输入生成图片。本来打算用imagemagick的&#xff0c;但是后来觉得这样前后端要搞两份不同的代码&#xff0c;然后imagemagick使用起来远没有canvas用起来顺手啊。So&#xff0c;最终决定就用Canvas搞定它了&#xf…

如何用illustrator做技术手册_做期货用什么技术指标分析?

来源&#xff1a;期汇股金作者&#xff1a;DC链接&#xff1a;做期货用什么技术指标分析&#xff1f;投资期货市场首先我们要有一套自己的技术分析&#xff0c;那么我们有什么样的技术指标分析最准确呢&#xff0c;没有最准确的技术指标&#xff0c;要看你运用的程度&#xff0…

根据字符串自动构造对应类

问题的起因是&#xff0c;我在做一个demo&#xff0c;有一个对象基类&#xff0c;以及一堆派生出的子对象&#xff0c;比如球体、立方体之类的对象。还有一个对象管理类&#xff0c;用于存储场景中的所有对象。那么在初始化的时候&#xff0c;代码是这么写的&#xff1a;class …

openshift k8s_带有DIY的Openshift上的Spring Boot / Java 8 / Tomcat 8

openshift k8sDIY盒带是一种实验性盒带&#xff0c;提供了一种在OpenShift上测试不受支持的语言的方法。 它提供了最小限度的自由形式的支架&#xff0c;将墨盒的所有细节留给了应用程序开发人员 。 这篇博客文章说明了结合了PostgreSQL服务的Spring Boot / Java 8 / Tomcat 8应…

都兰县第一中学计算机,都兰县第一中学教案.doc

PAGE \* MERGEFORMATPAGE \* MERGEFORMAT 1都兰县第一中学教案班级初一.班周次9时间45分钟课时2授课教师席得勋教学内容篮球&#xff1a;胸前双手传接球器 材篮球25个、栏架4个、垫子4个、长凳4个、标志桶4个教学目标运动参与目标:通过学习激发学生兴趣&#xff0c;使学生积极参…