生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用
全局初始化互斥量和条件变量(不全局也行,但至少要对线程启动函数可见,这样才能使用。)
static pthread_cont_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
使用互斥量锁住一块代码方法如下(默认忽略pthread开头的函数的错误检查,即类似 int s = pthread_xxx(...); if (s != 0) { printErrorMsg(s, ...); }这种代码)
pthread_mutex_lock(&mtx);
// TODO: 在这里添加自定义代码(被锁住的代码)
pthread_mutex_unlock(&mtx);
被锁住的代码是同步执行的,这里定义代码的执行时间段为[start, end],即一段代码开始执行的时刻为start,结束执行的时刻为end。
则任意两个线程执行lock和unlock之间的代码的时间段均不会重叠,也就是多线程执行被锁住的代码可以看做串行的,即总会有下述时间顺序:
A线程lock成功 >>> A线程执行被锁住的代码 >>> A线程unlock >>> B线程lock成功 >>> B线程执行被锁住的代码 >>> B线程unlock
条件变量的使用则是和互斥量挂钩的,若被锁住的代码存在等待操作,又不能确定等多久,此时就需要使用条件变量来等待。
举个生产者-消费者的例子,线程A为狗的活动,线程B为主人的活动。狗进食和主人添加狗粮2个行为是互斥的,即不能同时发生,必须有先后顺序,代码类似下面
void* consumer(void* arg)
{
while () { // 狗持续进食
pthread_mutex_lock(&mtx);
// TODO: 狗进食的代码
pthread_mutex_unlock(&mtx);
}
} void* producer(void* arg) // 主人的一次喂食
{
pthread_mutex_lock(&mtx);
// TODO: 主人喂食的代码
pthread_mutex_unlock(&mtx);
}
而逻辑是,狗进食必须判断狗粮是否有剩余,也就是说狗进食的代码类似这样
// 狗进食的代码
if (hasFood())
dog_eat();
这段代码被线程启动函数consumer的lock和unlock锁住,于是每次都要上锁 >>> 检查是否有食物 >>> 解锁,若主人长时间不喂食,狗的线程将会浪费大量系统资源在这几个不必要的步骤上面,因此狗的正确做法是在没有食物时等待,然后主人添加食物时通知狗去吃。类似进程通信的signal()和kill()函数。
等待过程在系统中相当于是阻塞,被阻塞的线程不会耗费系统资源。
而条件变量则是如此使用的,这种思路下的代码如下
void* consumer(void* arg)
{
while () { // 狗持续进食
pthread_mutex_lock(&mtx);
if (!hasFood())
pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒 dog_eat(); // 被唤醒则代表有食物了,可以吃了
pthread_mutex_unlock(&mtx);
}
} void* producer(void* arg) // 主人的一次喂食
{
pthread_mutex_lock(&mtx);
addFood();
pthread_mutex_unlock(&mtx); pthread_cond_signal(&cond); // 添加食物后发出信号,唤醒被阻塞的consumer函数
}
消费者线程执行pthread_cond_wait时,会暂时unlock(),这样其他线程(生产者)才能继续执行。
生产者线程执行pthread_cond_signal时,会唤醒至少一条因为pthread_cond_wait而阻塞的线程,假如被唤醒的是执行consumer()函数的线程,pthread_cond_wait会返回,然后线程重新lock()。
——于是,其实这里的代码是有问题的,假如有一个线程先被唤醒,并吃空了狗粮,再之后,另一个线程才被唤醒,dog_eat()就会失败,因为狗粮被吃完了。
因此被唤醒时需要再检查一次
if (!hasFood())
pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒 if (hasFood())
dog_eat(); // 被唤醒则代表有食物了,可以吃了
但是如果唤醒后hasFood()返回“假”时,我们没有处理而是继续执行,而正常逻辑是,没有食物的时候还是要等待,然后再被唤醒,如果被唤醒时还是没食物继续等待,所以正确的写法是用while循环
while (!hasFood())
pthread_cond_wait(&cond, &mtx); // 阻塞直到被唤醒 dog_eat(); // 被唤醒则代表有食物了,可以吃了
生产者-消费者问题:介绍POSIX线程的互斥量和条件变量的使用的更多相关文章
- 并发编程(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题
请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...
- 并发编程入门(二):分析Boost对 互斥量和条件变量的封装及实现生产者消费者问题
请阅读上篇文章<并发编程实战: POSIX 使用互斥量和条件变量实现生产者/消费者问题>.当然不阅读亦不影响本篇文章的阅读. Boost的互斥量,条件变量做了很好的封装,因此比" ...
- [转]一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程
一个简单的Linux多线程例子 带你洞悉互斥量 信号量 条件变量编程 希望此文能给初学多线程编程的朋友带来帮助,也希望牛人多多指出错误. 另外感谢以下链接的作者给予,给我的学习带来了很大帮助 http ...
- 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(二)
1.Linux“线程” 进程与线程之间是有区别的,不过linux内核只提供了轻量进程的支持,未实现线程模型.Linux是一种“多进程单线程”的操作系统.Linux本身只有进程的概念,而其所谓的“线程” ...
- 并发编程(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...
- POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t 和pthread_cond_t的一系列的封装.因此通过 ...
- 并发编程入门(一): POSIX 使用互斥量和条件变量实现生产者/消费者问题
boost的mutex,condition_variable非常好用.但是在Linux上,boost实际上做的是对pthread_mutex_t和pthread_cond_t的一系列的封装.因此通过对 ...
- boost 线程、互斥体、条件变量
1.任何技术都是针对特定场景设计的,也就是说,为了解决某个问题而设计的. 2.考虑下面一种场景:一个小旅馆,只有一个卫生间,有清洁人员,店主人,和旅客.卫生间用完之后,就会自动锁闭,必须取钥匙,才能进 ...
- 互斥量、条件变量与pthread_cond_wait()函数的使用,详解(一)
1. 首先pthread_cond_wait 的定义是这样的 The pthread_cond_wait() and pthread_cond_timedwait() functions are us ...
随机推荐
- 上下行分流下行负载方式和能ping通但不能打开
1 下行线路负载方式选择 目的端口+协议 否则有可能出现微信443端口图片打不开的情况. 2.彭ping通但是打不开的情况下将上行线路mtu值改小 由1500改为1450
- C#连接Oracle数据库查询数据
C#连接Oracle数据库可以实现许多我们需要的功能,下面介绍的是C#连接Oracle数据库查询数据的方法,如果您对C#连接Oracle数据库方面感兴趣的话,不妨一看. using System; u ...
- MoreEffectiveC++Item35(异常)(条款9-15)
条款9 使用析构函数防止内存泄漏 条款10 在构造函数中防止内存泄漏 条款11 禁止异常信息传递到析构函数外 条款12 理解"抛出一个异常''与"传递一个参数"或调用一个 ...
- 2017.10.23 Arduino Atmel EFM32低功耗监测
mark:因为最近一直调试(维护)客户的EFM32,和其他系列的Atmel芯片.对atmel不熟悉,做点功课. 1. 自建Arduino 最小系统(原来树莓派都是Atmel的芯片) http://ww ...
- cmakelist
cmake 添加头文件目录,链接动态.静态库 罗列一下cmake常用的命令. CMake支持大写.小写.混合大小写的命令. 1. 添加头文件目录INCLUDE_DIRECTORIES 语法: incl ...
- Win10 64位系统ADO方式操作数据库失败解决方法
VC操作Access数据库一般通过ODBC.ADO.DAO等方式,但在我的Win10 64位操作系统中,通过ADO方式操作数据库会失败,无法读取数据.解决方法:1.首先确保Win10操作系统ado目录 ...
- [AOP] 7. 一些自定义的Aspect - Circuit Breaker
Circuit Breaker(断路器)模式 关于断路器模式是在微服务架构/远程调用环境下经常被使用到的一个模式.它的作用一言以蔽之就是提高系统的可用性,在出现的问题通过服务降级的手段来保证系统的整体 ...
- flutter笔记1:VScode安装dart code插件踩坑记录
新手菜鸟一枚,想从产品转入技术坑,目标:移动端APP开发.最近听技术达人 飞狐 说flutter beta发布了,支持跨平台APP开发,各种强大易上手,于是乎零基础入坑~话说想提高英文水平的同学,请移 ...
- python 之头像上传,预览
上传头像 我们需要实现的效果是:当我们点击默认头像,用户可以进行选择要上传的头像文件,其原理就是头像的img标签与文件input(file类型)框重合或者关联,可以通过如下两种方式进行实现: 方法一l ...
- 【剑指offer】找出数组中任意一个重复的数字,C++实现
原创博文,转载请注明出处! # 题目 # 思路 对于长度为n的数组,范围为0~n-1的数字而言,如果不粗在重复数字,则排序后数组元素和数组角标相同.如果存在重复数字,则在排序的过程中会出现不同下标对应 ...