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

本文主要是介绍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;
}

这篇关于C语言pthread使用互斥锁及条件变量的跨进程多生产多消费模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/808623

相关文章

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java进程异常故障定位及排查过程

《Java进程异常故障定位及排查过程》:本文主要介绍Java进程异常故障定位及排查过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、故障发现与初步判断1. 监控系统告警2. 日志初步分析二、核心排查工具与步骤1. 进程状态检查2. CPU 飙升问题3. 内存

Linux中压缩、网络传输与系统监控工具的使用完整指南

《Linux中压缩、网络传输与系统监控工具的使用完整指南》在Linux系统管理中,压缩与传输工具是数据备份和远程协作的桥梁,而系统监控工具则是保障服务器稳定运行的眼睛,下面小编就来和大家详细介绍一下它... 目录引言一、压缩与解压:数据存储与传输的优化核心1. zip/unzip:通用压缩格式的便捷操作2.

使用Python实现可恢复式多线程下载器

《使用Python实现可恢复式多线程下载器》在数字时代,大文件下载已成为日常操作,本文将手把手教你用Python打造专业级下载器,实现断点续传,多线程加速,速度限制等功能,感兴趣的小伙伴可以了解下... 目录一、智能续传:从崩溃边缘抢救进度二、多线程加速:榨干网络带宽三、速度控制:做网络的好邻居四、终端交互

Python中注释使用方法举例详解

《Python中注释使用方法举例详解》在Python编程语言中注释是必不可少的一部分,它有助于提高代码的可读性和维护性,:本文主要介绍Python中注释使用方法的相关资料,需要的朋友可以参考下... 目录一、前言二、什么是注释?示例:三、单行注释语法:以 China编程# 开头,后面的内容为注释内容示例:示例:四

Go语言中nil判断的注意事项(最新推荐)

《Go语言中nil判断的注意事项(最新推荐)》本文给大家介绍Go语言中nil判断的注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.接口变量的特殊行为2.nil的合法类型3.nil值的实用行为4.自定义类型与nil5.反射判断nil6.函数返回的

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁