线程同步之 生产者消费者模型详解
前言
博主本来没打算讲这个比较前面的知识的(博主socket编程还有两个部分没讲,进程也才写完回收僵尸进程的三种方法,信号捕捉器也才完结),但是今天有朋友来问博主,什么是生产者消费者模型,所以博主就先为为数不多的朋友把生产者消费者模型讲一讲,希望大家能看懂(没有现成和锁知识的朋友不要急,这部分是写给有基础的朋友看的,这些知识博主都会慢慢的讲到)。
什么是模型?
模型就是要解决某个问题的固定方法或套路,所以有时候我们学会一点模型是很有必要的。
生产者消费者条件变量模型
线程同步典型的案例即为生产者消费者模型,而借助条件变量来实现这一模型,是比较常见的一种方法。假定有两个线程,一个模拟生产者行为,一个模拟消费者行为。两个线程同时操作一个共享资源(一般称之为汇聚),生产向其中添加产品,消费者从中消费掉产品。
换一句话来说就是,比如说你在学校上课,好不容易熬到了放学,此时你肯定会和你的小伙伴一起去食堂吃饭(土豪除外,土豪不去食堂),但是因为之前食堂停水停电,导致,你们放学了才开始做饭,所以现在就会面临一个问题。
当食堂大叔做好饭之后会把饭放在指定的饭盒里面,但是食堂大叔做饭的速度可能比不上你和你小伙伴吃饭的速度(或者大叔做饭的速度太快导致们根本吃不过大叔的做饭速度)。这也就是我们的生产者消费者模型了。
所以博主先列出一些概念,更容易使大家理解:
生产者:产生数据的一方(食堂大叔做饭)
消费者:处理数据的一方(你和你的小伙伴吃饭)
我们会遇到的问题
双方处理数据的速度可能不同,这样就会导致总由一方吃处于阻塞状态。(也就是博主上面所说的吃饭的问题)可能博主的逻辑不是很严谨,但是将就着看吧。
要用到的函数解析:
int pthread_cond_wait
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);函数作用:
1.阻塞等待条件变量cond(参1)满足
2.释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);1.2.两步为一个原子操作。
3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);我们要知道的是在调用这个函数之前我们要做前准备好的事情:
1.mutex_t mutex 定义互斥量
2.init 初始化
3.lock 加锁
4.cond_t has_product 定义条件变量
5.inti 初始化
pthread_cond_signal
int pthread_cond_signal(pthread_cond_t *cond);唤醒至少一个阻塞在条件变量上的线程
提到了这么多的东西了,接下来博主就给出生产者消费者模型的代码(里面有很详细的解释了,大家应该都能看懂,博主就不在做GIF动画了)
/*借助条件变量模拟 生产者-消费者 问题*/
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <stdio.h>/*链表作为公享数据,需被互斥量保护*/
struct msg {struct msg *next;int num;
};struct msg *head;
struct msg *mp;/* 静态初始化 一个条件变量 和 一个互斥量 完成 1 2 4 5 */
pthread_cond_t has_product = PTHREAD_COND_INITIALIZER;
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void *consumer(void *p)
{while(true){pthread_mutex_lock(&lock); //加锁 完成 3 while (head == NULL) { //头指针为空,说明没有节点 可以为if吗pthread_cond_wait(&has_product, &lock); //此时会阻塞等待 等生产者传入之后才会继续工作/* 1.阻塞等待条件变量cond(参1)满足 2.释放已掌握的互斥锁(解锁互斥量)相当于pthread_mutex_unlock(&mutex);注意: 1和2这两步为一个原子操作。3.当被唤醒,pthread_cond_wait函数返回时,解除阻塞并重新申请获取互斥锁pthread_mutex_lock(&mutex);*/}mp = head; head = mp->next; //模拟消费掉一个产品pthread_mutex_unlock(&lock); //解锁printf("-Consume ---%d\n", mp->num);free(mp); //create 和 free 要成双成对出现mp = NULL; sleep(rand() % 5); //随机随眠让 CUP 转移}
}void *producer(void *p)
{while(true) {mp = malloc(sizeof(struct msg));mp->num = rand() % 1000 + 1; //模拟生产一个产品printf("-Produce ---%d\n", mp->num);pthread_mutex_lock(&lock); //加锁(不用担心死锁,即使是消费者先调用,在pthread_cond_wait()之后也会将之前获得的锁给释放!//头插法mp->next = head;head = mp;pthread_mutex_unlock(&lock); //解锁pthread_cond_signal(&has_product); //将等待在该条件变量上的一个线程(生产者线程)唤醒sleep(rand() % 5); //随机随眠让 CUP 转移}
}int main(int argc, char *argv[])
{pthread_t pid, cid; //pid-->生产者 cid -->消费者srand(time(NULL));/* 调用各自的主控函数 */pthread_create(&pid, NULL, producer, NULL);pthread_create(&cid, NULL, consumer, NULL);//回收各自的线程!pthread_join(pid, NULL);pthread_join(cid, NULL);return 0;
}
有朋友会遇到这种问题:
我为什么写的不能实现同步呢?就只是一个生产者在那干活
博主认为最有可能的原因是因为时序竞态,这种解决办法很简单加sleep就行了。
或者是 博主上面的两个函数没使用好导致这种情况,要记住 wait等的是生产的信号,signal发的是生产者的信号去唤醒阻塞的消费者。
线程同步之 生产者消费者模型详解相关推荐
- 生产者消费者模型详解以及实现
生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦.消息队列等场景.在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一 ...
- 生产者消费者模型详解
生产者消费者模型 文章目录 生产者消费者模型 什么是生产者消费者模型 基于BlockingQueue的生产者消费者模型 单生产者单消费者模型 多生产者多消费者模型 什么是生产者消费者模型 生产者消费者 ...
- 生产者消费者模型---详解及代码实现
概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者 ...
- 生产者/消费者模型详解(基于Java)
title: 生产者消费者模型 tags: 多线程 synchronized 锁 wait() notify() 生产者/消费者模型原理以及代码实现 一.生产者/消费者模型原理 所谓的生产者消费者模型 ...
- C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)
前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题--生产者消费者模型,并给出 ...
- 线程同步 生产者消费者 java_Java线程同步:生产者-消费者 模型(代码示例)
public class ThreadSyn { public static void main(String[] args) { new ThreadSyn(); } public ThreadSy ...
- Qt之线程同步(生产者消费者模式 - QSemaphore)
简述 生产者将数据写入缓冲区,直到它到达缓冲区的末尾,此时,它将从开始位置重新启动,覆盖现有数据.消费者线程读取数据并将其写入标准错误. Semaphore(信号量) 比 mutex(互斥量)有 ...
- Qt之线程同步(生产者消费者模式 - QWaitCondition)
简述 生产者将数据写入缓冲区,直到它到达缓冲区的末尾,这时,它从开始位置重新启动,覆盖现有数据.消费者线程读取数据并将其写入标准错误. Wait condition(等待条件)比单独使用 mut ...
- 生产者消费者_【线程通信】生产者消费者模型
1生产者消费者模型介绍 生产者消费者模型,是每一个学习多线程的的人都需要知道的模型; 大致情况就是:有两个线程,一个负责生产产品,一个消费产品,两者公用同一块内存区域,也就是产品放在了同一块内存上面, ...
最新文章
- golang 判断文件或文件夹是否存在
- kafka-python 停止消费
- 电脑不能访问服务器指定端口6,windows server2008 无法访问本机及其他服务器的所有端口...
- LRU和LFU的区别
- php隐藏路径ngnix,thinkphp框架在nginx环境下去掉index.php路径显示
- 談JS面向對象【靜態與非靜態類】
- c#正则表达式取出数据库中带html标签的内容,C#用正则表达式 获取网页源代码标签的属性或值...
- 超级楼梯HDU2041
- 【代码笔记】iOS-切换条
- Android源码编译到/data/app方法
- 窗体传值,子窗体,父窗体,反射,reflection,windows,组策略,gpedit.msc,动态创建窗体,谢谢...
- 计算机共享怎么ip设置,如何设置网络打印机共享
- 如何在国外做好自然科学研究-2
- 微PE制作U盘启动盘步骤
- php3d饼状图的教学,php使用Jpgraph创建3D饼形图效果示例
- 上汽董事长称不接受与华为合作自动驾驶;曝OPPO给离职员工补发年终奖,此前遭克扣;Google Play 将启用AAB格式应用...
- 微信小程序播放bilibili视频
- 燕十八PHP高性能架构班Oracle部分课程
- 通过USB VID和PID卸载USB设备
- 响应式设计布局要不要了解一下?
热门文章
- Linux下TCP循环接收数据的方式
- 水刀切割设备行业调研报告 - 市场现状分析与发展前景预测
- 2019级C语言大作业 - HP1的勇者
- 电工结业试卷_电工学试题.doc
- 从入门到入土:基于Python|ACK|FIN|Null|Xmas|windows|扫描|端口扫描|scapy库编写|icmp协议探测主机|对开放端口和非开放端口完成半连接扫描|全连接扫描|
- 编程语言成功的几大要素
- 编写代码注释的最佳实践
- 自由软件不够吸引人?
- 红帽 与 CentOS 之间的恩怨情仇
- “我们的边缘计算技术点,可能超前了业界一点”