C语言pthread使用互斥锁及条件变量的跨进程多生产多消费模型

创作灵感:

格局要打开,进程也要打开,看看进程外的世界

代码描述:

1 此代码基于一个:

使用互斥锁及条件变量的线程间生产消费模型

该模型使用粗粒度锁 将整个生产或消费过程锁住

直到生产满 或 消费空 才使用条件变量通知对方

在唤醒后执行检查 只有仓库全满 或 仓库全空 才就继续执行生产消费逻辑 

否则依然是通知对方并等待

该模型同时还提供了一个监控线程 用于观察仓库情况

 2 变形记

将此线程间模型 分为两个进程(两个main函数)

使用posix风格的共享内存存放共享资源

使用pthread的互斥锁和条件变量对共享资源进行保护

为每个进程配置一个监视线程

并未实现2号生产者和消费者的逻辑

使用注意事项:

1 编译后在两个独立终端运行

2 不能使用大型IDE集成运行

3 unix-like系统 或 支持gnu_c的系统

4 对生产者按ctrl+c,生产消费都会清理资源并结束,输出all done

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/mman.h>// 速度基数,0.1秒
int tenth_s = 100000;
// 生产者1的速度
int producer2_speed = 5;
// 生产者2的速度
int producer1_speed = 5;
// 监视刷新速度
int monitor_speed = 5;
// 定义线程执行函数指针类型
typedef void *(*func)(void *);
// 互斥锁属性类型 只被初始化1次
pthread_mutexattr_t mutexattr;
// 条件变量属性类型 只被初始化1次
pthread_condattr_t condattr;
// 能装3个线程的数组
pthread_t tids[3] = {0};
// 用于共享内存中的数据,放到一个结构体里
struct shm_data
{pthread_cond_t cv_prod;pthread_cond_t cv_cons;pthread_mutex_t mutexlock;int warehouse[10];pid_t pid;
};
// 初始化共享内存后,返回的指针,转换成struct shm_data *类型
// 复制一份到全局变量,供整个进程使用
struct shm_data *global_addr;
// 这是生产者2的执行线程
void *producer2(void *p)
{// 现在不需要他,让他睡觉printf("producer2\n");sleep(999);pthread_exit(NULL);
}
// 这是收到信号后生产者1的清理函数
void clean_up(void *p)
{// 释放锁pthread_mutex_unlock(&global_addr->mutexlock);
}
// 这是生产者1的执行线程
void *producer1(void *p)
{printf("producer1\n");// 无限循环 套在最外面while (1){// 压入清理函数 防止意外终止pthread_cleanup_push(clean_up, NULL);// 上锁,注意是共享内存中的锁pthread_mutex_lock(&global_addr->mutexlock);// 用于记录仓库中产品的数量int count_p = 0;for (size_t i = 0; i < 10; i++){// 如果在某个位置上没货if (global_addr->warehouse[i] == 0){// 生产消耗时间usleep(producer1_speed * tenth_s);// 生产完毕global_addr->warehouse[i] += 1;}// 在某个位置有货记录+1if (global_addr->warehouse[i] == 1){count_p += 1;}}// 如果10次循环 每个位置都有货,表示仓库已满if (count_p == 10){// do...while用于检查唤醒后仓库的状态// 如果没有任何产品则继续生产,如果存在产品未消耗则通知消费者并等待do{// 告诉消费者可以消费了pthread_cond_signal(&global_addr->cv_cons);printf("producer %d wait\n", gettid());// 放锁,睡眠pthread_cond_wait(&global_addr->cv_prod, &global_addr->mutexlock);// 被唤醒后检查仓库状态count_p = 0;for (size_t i = 0; i < 10; i++){if (global_addr->warehouse[i] == 0){count_p += 1;}}} while (count_p != 10);// 真正醒来printf("producer %d wake up\n", gettid());}// 放锁,新一轮生产pthread_mutex_unlock(&global_addr->mutexlock);pthread_cleanup_pop(1);}pthread_exit(NULL);
}
// 这是一个人类可读的监视进程
void *show_warehouse(void *p)
{while (1){for (size_t i = 0; i < 10; i++){printf("[%d] ", global_addr->warehouse[i]);}printf("\n");// 刷新速度usleep(monitor_speed * tenth_s);}
}
// ctrl+c 信号处理函数
void sig_handler(int signum)
{// 打印信号描述printf("%s\n", strsignal(signum));
}
// 此函数用于初始化共享内存
void shm_init()
{// 打开共享内存文件描述符int fd = shm_open("/shm1", O_CREAT | O_RDWR, 0700);if (fd == -1){perror("shm_open");}// 扩容if (ftruncate(fd, 1024) == -1){perror("ftruncate");}// 附加到进程,并转换成struct shm_data *类型struct shm_data *addr = (struct shm_data *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (addr == MAP_FAILED){perror("mmap");}else{// 赋值全局变量,全局可用global_addr = addr;}
}
void ips_init()
{// 初始化mutexattrpthread_mutexattr_init(&mutexattr);// 设共享pthread_mutexattr_setpshared(&mutexattr, PTHREAD_PROCESS_SHARED);// 初始化mutexpthread_mutex_init(&global_addr->mutexlock, &mutexattr);// 初始化condattrpthread_condattr_init(&condattr);// 设共享pthread_condattr_setpshared(&condattr, PTHREAD_PROCESS_SHARED);// 初始化condpthread_cond_init(&global_addr->cv_cons, &condattr);pthread_cond_init(&global_addr->cv_prod, &condattr);// 初始化仓库memset(global_addr->warehouse, 0, sizeof(global_addr->warehouse));// 创建线程两个生产者,一个监视func fun_c[3] = {producer1, producer2, show_warehouse};int i = 0;for (i = 0; i < 3; i++){pthread_create(&tids[i], NULL, fun_c[i], NULL);}
}
int main()
{// 注册信号处理函数 sig_handlersignal(SIGINT, sig_handler);// 初始化共享内存,赋值给一个struct shm_data 类型的全局指针变量shm_init();// 一些把线程共享改成进程共享的准备工作ips_init();// 暂停主线程pause();// 关闭消费者进程kill(global_addr->pid, SIGINT);// 关闭生产线程int i;for (i = 0; i < 3; i++){pthread_cancel(tids[i]);pthread_join(tids[i], NULL);}// 销毁条件变量属性pthread_condattr_destroy(&condattr);// 销毁互斥锁属性pthread_mutexattr_destroy(&mutexattr);// 销毁互斥锁pthread_mutex_destroy(&global_addr->mutexlock);// 销毁条件变量pthread_cond_destroy(&global_addr->cv_prod);pthread_cond_destroy(&global_addr->cv_cons);// 与共享内存分离munmap(global_addr, 1024);// 删除共享内存shm_unlink("/shm1");// 全剧终printf("all done\n");return 0;
}
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include <sys/mman.h>
// 调速
int tenth_s = 100000;
int consumer_speed = 5;
int producer_speed = 5;
int monitor_speed = 5;pthread_t tids[3] = {0};
typedef void *(*func)(void *);
struct shm_data
{pthread_cond_t cv_prod;pthread_cond_t cv_cons;pthread_mutex_t mutexlock;int warehouse[10];pid_t pid;
};
struct shm_data *global_addr;
// 清理函数
void clean_up(void *p)
{pthread_mutex_unlock(&global_addr->mutexlock);
}
// consumer1线程执行函数
void *consumer1(void *p)
{printf("consumer1\n");while (1){pthread_cleanup_push(clean_up, NULL);pthread_mutex_lock(&global_addr->mutexlock);int count_c = 0;for (size_t i = 0; i < 10; i++){if (global_addr->warehouse[i] == 1){usleep(consumer_speed * tenth_s);global_addr->warehouse[i] -= 1;}if (global_addr->warehouse[i] == 0){count_c += 1;}}if (count_c == 10){do{pthread_cond_signal(&global_addr->cv_prod);printf("consumer %d wait\n", gettid());pthread_cond_wait(&global_addr->cv_cons, &global_addr->mutexlock);// 被唤醒后检查仓库状态count_c = 0;for (size_t i = 0; i < 10; i++){if (global_addr->warehouse[i] == 1){count_c += 1;}}// 如果仓库中所有位置都有商品,则通过检查,继续消费} while (count_c != 10);printf("consumer %d wake up\n", gettid());}pthread_mutex_unlock(&global_addr->mutexlock);pthread_cleanup_pop(1);}pthread_exit(NULL);
}
// consumer1线程执行函数
void *consumer2(void *p)
{printf("consumer2\n");sleep(999);pthread_exit(NULL);
}
// 这是一个人类可读的监视进程
void *show_warehouse(void *p)
{while (1){for (size_t i = 0; i < 10; i++){printf("[%d] ", global_addr->warehouse[i]);}printf("\n");usleep(monitor_speed * tenth_s);}pthread_exit(NULL);
}
// 共享内存初始化函数
void shm_init()
{int fd = shm_open("/shm1", O_CREAT | O_RDWR, 0700);if (fd == -1){perror("shm_open");}struct shm_data *addr = (struct shm_data *)mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (addr == MAP_FAILED){perror("mmap");}else{// 注意这里赋值了共享资源pid,用于在生产进程按ctrl+c时 同时关闭清理消费进程addr->pid = getpid();global_addr = addr;}
}
void pthread_create_init()
{func fun_c[3] = {consumer1, consumer2, show_warehouse};int i = 0;for (i = 0; i < 3; i++){pthread_create(&tids[i], NULL, fun_c[i], NULL);}
}
// 信号处理函数
void sig_handler(int signum)
{printf("%s\n", strsignal(signum));
}
int main()
{// 注册信号处理函数signal(SIGINT, sig_handler);// 初始化共享内存shm_init();// 创建进程pthread_create_init();// 主线程暂停pause();// 收到信号后运行下面代码int i;for (i = 0; i < 3; i++){pthread_cancel(tids[i]);pthread_join(tids[i], NULL);}// 与共享内存分离munmap(global_addr, 1024);printf("all done\n");return 0;
}

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

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

相关文章

java实现快速排序的方法

算法概念 快速排序&#xff1a;通过一趟排序将待排记录分隔成独立的两个部分&#xff0c;其中一部分记录的数据均比另一部分的数据小&#xff0c;则可分别对这两部分记录继续进行排序&#xff0c;以达到整个序列有序。 快速排序的最坏运行情况就是O&#xff08;n^2&#xff09…

手把手教你打造研究生个人简历模板|轻松驾驭简历设计

在简历设计中&#xff0c;如何展现自己的学历优势&#xff0c;是很多学生困扰的问题。 下面&#xff0c;我们首先从即时设计中分享一些不同风格的研究生简历模板。 风格多样的免费简历设计模板https://js.design/community?categorysearch&search%E7%AE%80%E5%8E%86&…

使用STM32+ESP8266(ESP-01S)+点灯科技(手机端Blinker)实现远程控制智能家居

硬件准备&#xff1a;STM32单片机、ESP8266&#xff08;ESP-01S&#xff09;、CH340C下载烧录器 软件准备&#xff1a;STM32CubeMX、Keil uVision5、Arduino IDE、 点灯科技&#xff08;手机端APP Blinker&#xff09;点灯科技 (diandeng.tech)点击进入 值得注意的是&#x…

【EfficientNetV2】《EfficientNetV2: Smaller Models and Faster Training》

google ICML-2021 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method4. 1 Understanding Training Efficiency4.2 Training-Aware NAS and Scaling4.3 Progressive learning 5 Experiments5.1 Datasets and Metrics5.2 ImageNet ILSVR…

力扣654 最大二叉树 Java版本

文章目录 题目描述解题思路代码 题目描述 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。 递归地在最大值 左边 的 子数组前缀上 构建左子树。 递归地在最大值 右边 的 子数组后缀上…

备战蓝桥杯Day25 - 二叉搜索树查询和删除操作

一、查询 递归查询 寻找的值比根节点大&#xff0c;遍历右子树&#xff1b; 寻找的值比根节点小&#xff0c;遍历左子树。 def qurey(self, node, val):if not node: # 没有节点&#xff0c;返回空return Noneif node.data < val:return self.qurey(node.rchild, val)el…

【Python】新手入门学习:详细介绍单一职责原则(SRP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍单一职责原则&#xff08;SRP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyT…

力扣106 从中序与后续遍历序列构造二叉树

文章目录 题目描述解题思路代码 题目描述 给定两个整数数组 inorder 和 postorder &#xff0c;其中 inorder 是二叉树的中序遍历&#xff0c; postorder 是同一棵树的后序遍历&#xff0c;请你构造并返回这颗 二叉树 。 示例 1: 输入&#xff1a;inorder [9,3,15,20,7], …

海豚调度系列之:认识海豚调度

海豚调度系列之&#xff1a;认识海豚调度 一、海豚调度二、特性三、建议配置四、名次解释 一、海豚调度 Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处理过…

使用 opencv 识别答题卡,生成填涂答案

一般答题卡设计时都在试卷4个角预留4个一样大小的黑块 仅能识别选择题判断题之类的填涂答题的题目&#xff0c;不能识别填空题应用题等其它主观题 使用 opencv 识别试卷图片中所有黑块&#xff0c;再根据黑块大小获取四个角的位置&#xff0c;根据四个黑块位置校正图像 将图…

openGauss安装与使用

一、opengauss 数据库安装&#xff1a; 1.1 实验环境&#xff1a;Virtual BOX 6.1.26centos 7.8openGauss1.1.0 1.2 虚拟机 Virtual BOX 安装&#xff1a; 在 virtualbox.org/wiki/Downloads 上下载 WINDOS hosts 版本的安装包并安装&#xff08;全部下一步默认安装&#xf…

Ubuntu下txt中文显示乱码问题常规解决方法

在正常使用ubuntu 文档时&#xff0c;突然发现txt文档出现&#xff0c;如下情况 无法正常观看&#xff0c;后来搜了一下发现是gedit 没有对应打开文件的编码格式&#xff0c;Ubuntu用的是utf-8,所以打开会有乱码&#xff01;初始没有GBK和GB2312&#xff0c;把GBK和GB2312添加…

vi 显示行号 显示色彩

首先进入当前用户目录下的.vimrc文件/.virc文件&#xff08;具体要看操作系统&#xff09; vi ~/.virc 显示行号 set number 显示色彩 highlight LineNr guifgred 效果

API 管理调研

当前大部分团队内 API 管理都是依赖 Postman&#xff0c;postman最大的问题是共享问题&#xff0c;如果我要使用另外一个人已经调试好的 API 非常麻烦。因此&#xff0c;能实现协作的 API 管理将极大提升效率。 Yapi https://github.com/YMFE/yapi https://hellosean1025.gi…

Python中requests、aiohttp、httpx性能对比

在Python中&#xff0c;有许多用于发送HTTP请求的库&#xff0c;其中最受欢迎的是requests、aiohttp和httpx。这三个库的性能和功能各不相同&#xff0c;因此在选择使用哪个库时&#xff0c;需要考虑到自己的需求和应用场景。 首先&#xff0c;让我们来了解一下这三个库的基本…

怎样才能选择一套好的ai智能语音系统呢?人工智能电话机器人系统各版本搭建

如今竞争激烈的市场环境下&#xff0c;企业需要不断创新&#xff0c;提高效率&#xff0c;以获得更多的市场份额和利润。而智能电销机器人&#xff0c;作为一种新型的营销工具&#xff0c;正逐渐成为越来越多企业的首选工具&#xff0c;接下来我们看看怎样才能选择一套好的ai智…

生态碳汇涡度相关监测与通量数据分析

朱老师&#xff08;副教授&#xff09;&#xff1a;来自国内重点高校&#xff0c;长期从事涡度通量观测与分析研究&#xff0c;发表SCI论文多篇&#xff0c;主持国家与地方科研项目多个&#xff0c;在生态环境数据处理与分析中具有丰富的实践项目经验。 本文基于MATLAB语言、以…

滑动窗口最大值(leetcode hot100)

给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3,-1,-3,5,3,6,7], k 3 输…

2024年初中生古诗文大会备考:多选题真题和独家解析(持续更新)

今天我们继续来做初中古诗文大会的一道难题&#xff1a;多选题&#xff0c;让大家了解初中生古诗文大会的考察内容和形式&#xff0c;并且提供了我独家的题目解析和答案&#xff0c;供初中的同学们学习和参考。 Tips&#xff1a;古诗文大会的许多题目都来自于中考、高考&#…

使用Barrier共享鼠标键盘,通过macos控制ubuntu系统

之前文章写过如何使用barrrier通过windows系统控制ubuntu系统&#xff0c;该文章将详细介绍如何使用barrier通过macos系统控制ubuntu系统 一、macOS安装barrier macOS版本barrier链接 1、双击点开安装包 2、将安装包里的barrier拷贝到macOS的达达->应用程序中 3、在达达…