生产者与消费者模式(线程的同步与互斥)

2023-11-13

条件变量

条件变量的提出首先要涉及一个概念,就是生产者消费者模型:


生产者消费者,是在多线程同步的一个问题,两个固定大小缓冲区的线程,在实际运行是会发生问题,生产者是生成数据放入缓冲区,重复过程,消费者在缓冲区取走数据。

生产者消费者的模型提出了三种关系,两种角色,一个场所

三种关系: 
- 生产者之间的互斥关系 
- 消费者之间的竞互斥关系 
- 生产者和消费者之间互斥和同步关系(同一时刻只能有一个,要么在生产,要么在消费,这就是互斥关系,只能在生产者生产完了之后才能消费,这就是同步关系)

两个角色:一般是用进程或线程来承担生产者或消费者

一个场所:有效的内存区域。(如单链表,数组)
我们就可以把这个想象成生活中的超市供货商,超市,顾客的关系,超市供货商供货,超市是摆放货物的场所,然后用户就是消费的。
条件变量属于线程的一种同步的机制,条件变量与互斥锁一起使用,可以使得线程进行等待特定条件的发生。条件本身是由互斥量保护的,线程在改变条件状态之前首先会锁住互斥量。其他线程在获得互斥量之前不会察觉这种改变,因此互斥量锁定后才能计算条件。


和互斥锁一样,使用条件变量,同样首先进行初始化:


int pthread_cond_init(pthread_cond_t *restrict cond,
              const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

和互斥锁的初始化一样,它也可以采用init或者是直接利用宏进行初始化。

条件变量本身就是依赖互斥锁的,条件本身是由互斥量保护的,线程在改变条件状态钱先要锁住互斥量,它是利用线程间共享的全局变量进行同步的一种机制。

我们使用pthread_cond_wait进行等待条件变量变为真,如果在规定的时间不能满足,就会生成一个返回错误码的变量。


 int pthread_cond_wait(pthread_cond_t *restrict cond,
              pthread_mutex_t *restrict mutex);
 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

把锁传递给wait函数,函数自动把等待条件的线程挂起,放入消费者等待队列,然后解锁挂起线程所占有的互斥锁,这个时候就可以去跑其他线程,然后当等待条件满足的时候,这个时候从等待队列中出来执行,获得刚才自己所占有的锁。

一个线程可以调用pthread_cond_wait在一个Condition Variable上阻塞等待,这个函数做以下三步操作:
1. 释放Mutex
2. 阻塞等待
3. 当被唤醒时,重新获得Mutex并返回

满足条件的时候可以使用函数pthread_cond_signal进行唤醒

      int pthread_cond_broadcast(pthread_cond_t *cond);
       int pthread_cond_signal(pthread_cond_t *cond);

 
 
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

这两个函数都是用来进行唤醒线程操作的,signal一次从消费者队列中至少唤醒一个线程,broad_cast能唤醒等待该条件的所有线程。

当然和mutex类似,条件变量也需要清除。

int pthread_cond_destroy(pthread_cond_t *cond);
生产者消费者示例:

#include<stdio.h>
#include<assert.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

typedef struct Node
{
	struct Node *next;
	int val;
}Node;

void list_init(Node **phead)
{
	assert(phead);

	Node *temp = (Node*)malloc(sizeof(Node));
	temp->next = NULL;
	temp->val = 0;
	*phead = temp;
}

void list_push(Node *phead,int _data)  //头插
{
	assert(phead);

	Node *pM = (Node*)malloc(sizeof(Node));
	if (pM)
	{
		pM->val = _data;
		pM->next = phead->next;
		phead->next = pM;
	}
}

int empty(Node *phead)
{
	return (phead->next == NULL ? 1: 0);
}

void list_print(Node *phead)
{
	assert(phead);

	if (empty(phead))
		return ;

	Node *pCur = phead->next;
	while (pCur)
	{
		printf("%d->",pCur->val);
		pCur = pCur->next;
	}
	printf("%s\n","NULL");
}

void list_pop(Node *phead,int *data)  //头删
{
	assert(phead);
	
	if (empty(phead))
		return ;

	Node *ptemp = phead->next;

	phead->next = ptemp->next;

	*data = ptemp->val;
	free(ptemp);

	ptemp = NULL;
}

void list_destroy(Node *phead)
{
	assert(phead);

	while (!empty(phead))
	{
		int data;
		list_pop(phead,&data);
	}
}
//生产者线程
void* producer(void *arg)
{
	Node *phead = (Node *)arg;
	while (1)
	{
		pthread_mutex_lock(&mutex);  //申请互斥锁
		int data = rand()%100;
		list_push(phead,data);
		pthread_mutex_unlock(&mutex);//释放互斥锁
		printf("prodecer sucess %d\n",data);
		pthread_cond_signal(&cond); //以单播的方式通知拥有条件变量的另外一个线程,告诉消费者,生产者生产好了,可以消费了。
		sleep(1);
	}

	return NULL;
}

//消费者线程
void* consumer(void *arg)
{
	Node *phead = (Node *)arg;
	while (1)
	{
		int data;

		pthread_mutex_lock(&mutex);
		
		if (empty(phead)) //如果没有资源可以消费了,则等待
		{
			pthread_cond_wait(&cond,&mutex); //这个函数调用一定是在拥有互斥锁的前提下.这个函数做三件事,第一:释放互斥锁,二,阻塞等待,三,唤醒的时候重新获得互斥锁。
		}
		list_pop(phead,&data);//有资源就消费
		pthread_mutex_unlock(&mutex);
		printf("consumer sucess %d\n",data);
	}
	return NULL;
}

int main()
{
	Node *phead;
	list_init(&phead);

	pthread_t id1;
	pthread_t id2;


	pthread_create(&id1,NULL,producer,(void*)phead);
	pthread_create(&id2,NULL,consumer,(void*)phead);


	pthread_join(id1,NULL);
	pthread_join(id2,NULL);


	pthread_mutex_destroy(&mutex);
	pthread_cond_destroy(&cond);
	
	return 0;
}

信号量

信号量相关函数:

信号量(Semaphore)Mutex类似,表示可用资源的数量,Mutex不同的是这个数量可以大于1

#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);

sem_init() 初始化一个定位在 sem 的匿名信号量。value 参数指定信号量的初始值。 pshared 参数指明信号量是由进程内线程共享,还是由进程之间共享。如果 pshared 的值为 0,那么信号量将被进程内的线程共享,并且应该放置在所有线程都可见的地址上(如全局变量,或者堆上动态分配的变量)。如果 pshared 是非零值,那么信号量将在进程之间共享


int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);//类似P操作
int sem_trywait(sem_t *sem);
int sem_post(sem_t *sem);//类似V操作



用数组模拟环形队列存储数据代码:

#include<stdio.h>
#include<stdio.h>
#include<pthread.h>
#include<unistd.h>
#include<semaphore.h>

//二个规则,(1)消费者一定要等到生产才能消费,(2)生产者如果生产的快的话,不能套消费者一个圈

#define SIZE 64

sem_t blanks; //二个信号量
sem_t datas;
int buf[SIZE]; //用数组模拟一个环形队列。


void *productor(void *arg)
{
	int i = 0; //i = 0表示生产的起始位置。
		while (1)
		{
			//	pthread_mutex_lock(&mutex_p);
			sem_wait (&blanks);//生产者在生产之前要有格子资源。
			int data = rand()%1234;
			buf[i] = data;
			printf("productor done...data:%d\n",data);
			i++;
			i%=SIZE;//i++总有超过数组的长度的时候,为了模拟环形队列,所以求模。
			sleep(1); //生产的慢点
			sem_post(&datas);//生产者生产完了,数据资源就多了一个。
			//	pthread_mutex_unlock(&mutex_p);
		}
		return NULL;
}
	
void *consummer(void *arg)
{
	int i = 0;
	while (1)
	{										
	sem_wait(&datas); //消费者在消费之前要有消费资源。
	int data = buf[i];
	printf("consummer done...data:%d\n",data);
	i++;
	i%= SIZE;
	sem_post(&blanks);//消费者消费完了格子资源就多了一个。
	}
	return NULL;
}

	
int main()
{
	sem_init(&blanks,0,SIZE);
	sem_init(&datas,0,0);   //信号量的初始化要在多线程之前

	pthread_t id1,id2;
	pthread_create(&id1,NULL,productor,NULL);	
	pthread_create(&id2,NULL,consummer,NULL);

	pthread_join(id1,NULL);
	pthread_join(id2,NULL);


	sem_destroy(&blanks);
	sem_destroy(&datas);

	return 0;									
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

生产者与消费者模式(线程的同步与互斥) 的相关文章

  • 我们如何在使用循环时调用 ansible playbook 中的变量

    我有两个文件 其中这些文件包含server names and server IP s 我想更改 替换一些特定的server names and IP addressees根据要求在两个文件中 这与这篇文章 因为它被要求开设一个新职位 ht
  • 如何通过 makefile 在 Linux 上安装程序? [复制]

    这个问题在这里已经有答案了 可能的重复 Linux Unix make install 应该包含什么 https stackoverflow com questions 528399 what should linux unix make
  • 如何通过ssh获取远程命令的退出代码

    我正在通过 ssh 从远程计算机运行脚本 ssh some cmd my script 现在 我想在本地计算机上存储 shell 脚本的退出状态 我该怎么做 假设没有任何问题ssh其本身 其退出状态是在远程主机上执行的最后一个命令的退出状态
  • 选择fasta文件中氨基酸超过300个且“C”出现至少4次的序列

    我有一个包含蛋白质序列的 fasta 文件 我想选择超过 300 个氨基酸且半胱氨酸 C 氨基酸出现超过 4 次的序列 我使用此命令来选择具有超过 300 个 aa 的序列 cat 72hDOWN fasta fasta bioawk c
  • Linux 内核使用的设备树文件 (dtb) 可视化工具? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个可以图形化表示Linux内核中使用的硬件设备树的工具 我正在尝试了解特定 Arm 芯片组
  • 使用 shell 脚本发送 HTML 邮件

    如何使用 shell 脚本发送 HTML 电子邮件 首先 您需要撰写消息 最低限度由这两个标头组成 MIME Version 1 0 Content Type text html 以及适当的消息正文 p Hello world p 获得后
  • 在 scapy 中通过物理环回发送数据包

    我最近发现了 Scapy 它看起来很棒 我正在尝试查看 NIC 上物理环回模块 存根上的简单流量 但是 Scapy sniff 没有给出任何结果 我正在做的发送数据包是 payload data 10 snf sniff filter ic
  • Docker忽略limits.conf(试图解决“打开文件太多”错误)

    我正在运行一个 Web 服务器 该服务器正在处理数千个并发 Web 套接字连接 为了实现这一点 在 Debian linux 我的基本镜像是 google debian wheezy 在 GCE 上运行 上 打开文件的默认数量设置为 100
  • Ruby:在 Ubuntu 上安装 rmagick

    我正在尝试在 Ubuntu 10 04 上安装 RMagick 看起来here https stackoverflow com questions 1482823 is there an easy way to install rmagic
  • 正则表达式删除块注释也删除 * 选择器

    我正在尝试使用 bash 从 css 文件中删除所有块注释 我有以下 sed 命令的正则表达式 sed r s w s w d 这可以很好地去除块注释 例如 This is a comment this is another comment
  • LINUX:如何锁定内存中进程的页面

    我有一个 LINUX 服务器 运行一个具有大量内存占用的进程 某种数据库引擎 该进程分配的内存太大 需要将其中一部分换出 换出 我想做的是将所有其他进程 或正在运行的进程的子集 的内存页面锁定在内存中 以便只有数据库进程的页面被换出 例如
  • Ubuntu Python shebang 线不工作

    无法让 shebang 线在 Ubuntu 中为 python 脚本工作 我每次只收到命令未找到错误 test py usr bin env python print Ran which python usr bin python 在 sh
  • 如何在线程创建和退出时调用函数?

    include
  • 如何在不使用 IDE 的情况下在 Linux 上运行 Java 项目

    我是 Java 新手 基本上 我开发了一个java项目 其中包含Eclipse中的多个Java包 该项目在我安装了 redhat Linux 的桌面上运行正常 然而 我需要在一个更强大的没有安装X11的Linux服务器 redhat ent
  • 找不到包“gdk-pixbuf-2.0”

    我正在尝试在 Amazon Linux 发行版实例上构建 librsvg 我已经通过 yum 安装了大部分依赖项 其中一些在实例上启用的默认 yum 存储库中不可用 因此必须从头开始构建它们 我已经走了很远 但还停留在最后一点 跑步时sud
  • 为什么 Linux 没有 DirectX API?

    在考虑现代显卡的 Windows 系统上 DirectX API 的驱动程序端实现时 我想知道为什么此实现在非 Windows 系统 尤其是 Linux 上不可用 由于明显缺乏此功能 我只能假设有一个我无视的充分理由 但在我的原始理解中 我
  • 适用于 Linux 的轻量级 IDE [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • docker 非 root 绑定安装权限,WITH --userns-remap

    all 尝试让绑定安装权限正常工作 我的目标是在容器中绑定安装卷 以便 a 容器不以 root 用户身份运行入口点 二 docker daemon 配置了 userns remap 这样容器 主机上没有 root c 我可以绑定挂载和读 写
  • python获取上传/下载速度

    我想在我的计算机上监控上传和下载速度 一个名为 conky 的程序已经在 conky conf 中执行了以下操作 Connection quality alignr wireless link qual perc wlan0 downspe
  • 使用 \r 并打印一些文本后如何清除控制台中的一行?

    对于我当前的项目 有一些代码很慢并且我无法使其更快 为了获得一些关于已完成 必须完成多少的反馈 我创建了一个进度片段 您可以在下面看到 当你看到最后一行时 sys stdout write r100 80 n I use 80覆盖最终剩余的

随机推荐

  • ubuntu下设置网络

    修改文件 etc network interfaces 命令 sudo vi etc network interfaces 或 sudo gedit etc network interfaces 我的配置如下 interfaces 5 fi
  • QT的信号槽的四种写法和五种链接方式

    目录 四种信号槽写法 五种连接方式 实例 常见错误及改正 错误1 未连接信号与槽 错误2 信号和槽参数不匹配 错误3 未使用Q OBJECT宏 错误4 跨线程连接未处理 在Qt中 信号 Signal 和槽 Slot 是一种用于对象之间通信的
  • 对于搞钱我们是认真的

    前言 大家好 我是xiezhr 一提到搞钱 想必大家都非常非常感兴趣 立马就精神抖擞了 说实话 在这疫情爆发这几年里 赚点钱真不容易 不知道你是不是也跟我一样 一个人在夜深人静的时候就在想怎么才能通过自己程序员的身份来搞钱呢 今天 我们就来
  • 基于JAVA的图书馆书库管理系统

    源码下载 https wwa lanzous com iMbYtmlznri 更多源码 http byamd xyz 论文
  • 数学建模课程

    数学规划模型 2020 3 7 1 数学规划 简称最优化问题 2 最优化问题的数学模式的一般形式 三个要素 决策变量 目标函数 约束条件 所确定的x的范围为 可行域 满足 2 的解 可行解 同时满足 1 2 最优解 整个可行域上 全局最优解
  • TensorFlow2.x,GPU代码测试

    TensorFlow2 x GPU代码测试 代码如下 import tensorflow as tf import os os environ TF CPP MIN LOG LEVEL 2 不显示等级2以下的提示信息 print GPU t
  • 测试开发工程师需要掌握什么技能?

    如何理解测试开发 有人问测试开发是测试还是开发 本质上来说 还是测试 因为测试是核心 开发是手段 那么说 成为测试开发 需要那些必备的技能呢 不同level的测试工程师应具备的基本技能第一个 我们称之为测试员 测试工程师 直白一点说 就是干
  • pandas学习笔记(三)---重建索引与处理缺失值

    本文介绍pandas中重建索引与处理缺失值 文章目录 一 重建索引 1 1series重建索引 1 2 dataframe重建索引并修改缺失值 二 处理缺失值 2 1丢掉含有缺失值的行或者列 2 2 填充缺失值 2 3判断df中每个元素是否
  • QT设置背景图片的3种方式 & 区别——设置样式表styleSheet

    方式1 修改MainWindow主窗口 的 styleSheet 1 效果图 工具栏和状态栏 也加了背景 参考链接https blog csdn net mozai147 article details 84112744 2 选中编辑Mai
  • Angular学习笔记69:Angular项目的单元测试 -- 对服务进行测试

    对服务进行测试 服务通常是单元测试中最简单的文件类型 对于简单 没有其他依赖的服务 对于一个没有其他依赖的服务 Mock 一些数据 进行测试即可 例如 对于一个公共数据处理的服务 import Injectable from angular
  • mysql——锁

    三种锁问题 1 丢失修改 火车票问题 让第一个人丢失了修改 2 不可重复读 幻象 第二次读会读到不正确的数据 一个事务读到另一个已提交的事务 3 读 脏 数据 A修改值后又回滚 B读到修改后的值 即脏数据 或者是一个事务读到了另一个事务未提
  • office版本比较

    office LTSC 2021 专业增强版就是简化版的永久版的OFFICE 365罢了 建议还是装365划算 装的什么版本无所谓 激活的时候用的什么许可证才是决定你使用时版本的最终因素 比如装的 office 2021 最后用的2016的
  • ibm多服务器共用显示模块连接,IBM WebSphere 应用服务器多个模块跨站脚本漏洞

    IBM WebSphere 应用服务器多个模块跨站脚本漏洞 受影响系统 IBM Websphere Application Server 7 0 IBM Websphere Application Server 6 1 不受影响系统 IBM
  • 百度地图API取消点击景点弹出框

    有时候我们自己在地图上添加的标注会跟百度地图上面的景点商家标注重合 点击的时候就会弹出景点弹窗 就像这样 我们正常在创建地图的时候都是 var map new BMap Map allmap 创建Map实例 想要禁用百度地图景点弹窗需要添加
  • unity3d 学习笔记_____Native2d 刚体、碰撞器、关节的使用

    Mass Mass of the rigidbody Linear Drag Drag coefficient affecting positional movement Angular Drag Drag coefficient affe
  • [C语言】多种方法求2的n次方

    方法一 循环法 不断地乘以2 循环i次 int main int a i c 1 注意要初始化为1而不是0 scanf d i for a 1 a lt i a c c 2 printf d c return 0 方法二 左移法 一个数它左
  • unity 移动物体到指定位置的四种方法

    方法1 使用Vector3 MoveTowards void Update float step speed Time deltaTime gameObject transform localPosition Vector3 MoveTow
  • intellij idea远程调试

    前景 1 有些时候 调试不得不用外网 比如说做支付宝的支付接口 服务器后台通 知就不得不用外网的ip 无奈 只能扔到服务器远程调试了 2 java后台和app结合开发 app测试出了一些问题 又比较难重现 那么这个时候远程调试就可以派上用场
  • IP地址分类和数据包传输过程用到的协议

    ip地址分类 单播地址 A 0XXX XXXX 0 127 1 126 B10XX XXXX 128 191 C110X XXXX 192 223 组播地址 D1110 XXXX 224 255 私有地址 E1111 XXXX 240 25
  • 生产者与消费者模式(线程的同步与互斥)

    条件变量 条件变量的提出首先要涉及一个概念 就是生产者消费者模型 生产者消费者 是在多线程同步的一个问题 两个固定大小缓冲区的线程 在实际运行是会发生问题 生产者是生成数据放入缓冲区 重复过程 消费者在缓冲区取走数据 生产者消费者的模型提出