模型中,最为关键的步骤是,在生产者回调函数中,未生产之前,消费者回调函数是阻塞的,阻塞方式是条件变量。
那么不使用条件变量,如何使用“信号量”实现阻塞呢?
答案是因为调用 sem_wait,当 sem == 0 时候,该线程就会阻塞。因此:生产者对应一个信号量 :sem_t produce;消费者对应一个信号量 :sem_t customer。
sem_init(&produce, 0, 2);
sem_init(&customer, 0, 0); 消费者 value = 0,表示被阻塞

// 头文件
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>
sem_t produce; // 定义2个信号量
sem_t custom;
typedef struct node // 定义共享变量结构体
{int data;struct node* next;
}Node;
Node* phead = NULL; // 头结点置空
// 生产者回调函数
void* produce_fun()
{while(1){sem_wait(&produce); // 生产者取信号量并生产Node* node = (Node*)malloc(sizeof(Node));node->data = rand() % 1000;node->next = phead;phead = node;printf("生产者:%lu, 产品:%d\n", pthread_self(), node->data);sem_post(&custom); // 给消费者信号量sleep(1);}return NULL; }
// 消费者回调函数
void* custom_fun()
{while(1){sem_wait(&custom); // 刚开始消费者阻塞,直至生产者给消费者信号量后,解除阻塞Node* del = phead; // 记录待删除头结点的位置phead = phead->next; // 更新删除后新头结点printf("消费者: %lu, 消费: %d\n", pthread_self(), del->data);free(del); // 删除头结点sem_post(&produce); // 归还信号量,回复阻塞状态sleep(1);}return NULL; }
int main()
{srand(time(NULL));// 初始化信号量sem_init(&produce, 0, 2);sem_init(&custom, 0, 0);// 创建线程pthread_t p1, p2;pthread_create(&p1, NULL, produce_fun, NULL);pthread_create(&p2, NULL, custom_fun, NULL);// 回收子线程pthread_join(p1, NULL);pthread_join(p2, NULL);// 销毁信号量sem_destroy(&produce);sem_destroy(&custom);return 0;
}

Linux学习之系统编程篇:使用信号量实现“生产者和消费者模型”相关推荐

  1. Linux学习之系统编程篇:信号量(sem_init / wait / trywait / post / destroy)

    一.信号量的初步认识 可以把信号量理解为加强版的互斥锁 对于互斥锁: pthread_mutex_init () 初始化 一把互斥锁后 :mutex = 1 ,代表有一把互斥锁可用. pthread_ ...

  2. Linux学习之系统编程篇:ps 和 kill 命令以及父子进程间数据共享模式

    一.ps 和 kill 命令 1.ps 命令 常用方式: ps aux :查看正在运行进程信息(主要查 pid). ps ajx :更加详细(PID. PPID:父进程 id. PGID:进程组 id ...

  3. Linux学习之系统编程篇:对线程的基本认识

    (1)fork()后创建的子进程是父进程的拷贝,那么pthread_create,创建线程,创建的线程跟原进程有什么关系呢? fork 会通过拷贝产生新的虚拟地址空间(PCB 会变化),而 pthre ...

  4. Linux学习之系统编程篇:编写一个守护进程

    需求:写一个守护进程,每隔 2s 获取一次系统时间,将这个时间写入到磁盘文件 #include <stdio.h> #include <stdlib.h> #include & ...

  5. Linux学习之系统编程篇:守护进程(精灵进程、后台进程)

    一.背景 一般情况下,启动终端(shell),系统会创建一个会话(shell 进程是会长),经过后续各种操作,该会话中会存在多个进程组,每个进程组中也会有多个进程(父进程是组长),若此时关闭 shel ...

  6. Linux学习之系统编程篇:shm 共享内存及其操作函数

    一.shm 和 mmap 的区别 (1)mmap 是在磁盘上建立一个文件,每个进程地址空间中开辟出一块空间进行映射.shm 每个进程最终会映射到同一块物理内存.shm 保存在物理内存,这样读写的速度最 ...

  7. Linux学习之系统编程篇:IPC 和管道的基本概念及管道的创建

    一.IPC 概念 IPC: 进程间通信. 进程间通信的常见的 4 中方式: (1)管道 pipe fifo :最简单(但只能在有血缘关系下进行). (2)信号 signal : 属于系统的,所以系统开 ...

  8. Linux学习之系统编程篇:exec 函数族

    函数能力:"换核不换壳"(能够替换进程虚拟地址空间中.text 代码段). 作用:让父子进程执行不相干的操作. 效果:有一个运行的程序 A,在 A 中调用另一个程序 B,程序有父子 ...

  9. Linux学习之系统编程篇:MMU(Memory Manager Unit 内存管理单元)

    一.虚拟内存地址 对应于上图的两端,其中 0 - 3G 是用户区 ,3 - 4G 是内核区.编码的内存地址都是虚拟地址. 在3G到4G之间是PCB 进程控制块.从3G到0依次为: (1)命令行参数 和 ...

最新文章

  1. 笔记-项目进度管理-精简
  2. SQL_Server_2008完全学习之第八章Transact-SQL编程
  3. strcpy函数的实现
  4. 心电信号越界怎么回事_心电监护仪常见故障分析与排除
  5. 多個不同格式文件如何合並至一個PDF檔
  6. left join缺失右括号_LeetCode刷题实战31:最长有效括号
  7. java camel swagger,Swagger将下划线转换为camelcase
  8. linux中 字符串,linux内核驱动中对字符串的操作
  9. python编写登录接口_每日一题.PYTHON编写简单登录接口?
  10. 浅谈:字符串、时间格式的转换
  11. WIN7开机欢迎界面后黑屏
  12. 主曲率、平均曲率、高斯曲率、法曲率、主方向
  13. 6,美国2012年总统候选人政治献金数据分析
  14. Endnote 参考文献格式设置:字体 字号 行距 悬挂缩进
  15. 【自然语言处理】【ChatGPT系列】大模型的涌现能力
  16. excel单元格内容拆分_拆分单元格内容,表哥表妹不要哭
  17. 考研数据结构之线性表(1.7)——练习题之分离单链表的奇数偶数(C表示)
  18. pandas 向已有的excel指定的行和列添加数据
  19. 软件测试 | 生命周期
  20. JAVA SSM框架黄淮学院食堂仓库管理系统的设计与实现源码

热门文章

  1. 推翻Hinton NeurIPS论文结论!审稿人评价:该文章在标签平滑和知识蒸馏的关系上取得了重大突破!...
  2. 无监督领域迁移及文本表示学习的相关进展
  3. OBS显示器捕获黑屏的解决方法
  4. mysql format不要逗号,mysql格式化小数
  5. C#——实现IComparableT 接口,ArrayLIst调用ArrayLIst.Sort()抛出System.InvalidOperationException异常解决方案
  6. Vue + Element UI——对话框式登录框DEMO
  7. 安卓CheckBox实现单选
  8. 线性代数 matlab,实用大众线性代数(MATLAB版)
  9. git回退到之前版本和合并分支查看当前分支切换分支
  10. 计算机考研【211 计算机专业院校 官方排名】教育部 第4次“计算机专业”学科评估