【项目 线程4】3.12生产者消费者模型 3.13条件变量 3.14信号量 C++实现生产者消费者模型

news2024/11/16 9:18:07

3.12生产者消费者模型

生产者消费者模型中的对象:
1、生产者
2、消费者
3、容器
若容器已满,生产者阻塞在这,通知消费者去消费;若容器已空,则消费者阻塞,通知生产者去生产。生产者可以有多个,消费者也可以有多个。容器中的数据是多个线程共享的,线程同步问题涉及到互斥量、读写锁等。
条件变量、信号量等。
在这里插入图片描述

/*
    生产者消费者模型(粗略的版本),此处不考虑容器存满
*/
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

// 创建一个互斥量
pthread_mutex_t mutex;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点(头插法),添加到链表中
    while(1) {
        pthread_mutex_lock(&mutex);
        //malloc默认返回void *,类型转换为struct Node *
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        pthread_mutex_unlock(&mutex);
        usleep(100);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针,记录要删除的结点信息
        struct Node * tmp = head;

        // 判断是否有数据,从头部开始删除
        if(head != NULL) {
            // 有数据
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            usleep(100);
        } else {
            // 没有数据,需进行解锁,不然讲产生死锁
            pthread_mutex_unlock(&mutex);
        }
    }
    return  NULL;
}

int main() {

    //初始化互斥量
    pthread_mutex_init(&mutex, NULL);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    //分离后的子线程可自行释放资源,不需其他线程对其进行回收释放
    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

	
    while(1) {
        sleep(10);
    }

    //释放互斥量
    pthread_mutex_destroy(&mutex);

    pthread_exit(NULL);

    return 0;
}


显示结果:
在这里插入图片描述后续课程解决当前生产者消费者模型所存在的问题!互斥量、条件变量、信号量

3.13条件变量

在这里插入图片描述在这里插入图片描述

在这里插入图片描述
条件变量,某个条件满足后引起阻塞或某个条件满足后解除阻塞。配合互斥锁实现线程同步。

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

// 创建一个互斥量,解决线程同步问题
pthread_mutex_t mutex;
// 创建条件变量,解决没有数据时,阻塞
pthread_cond_t cond;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中
    while(1) {
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        
        // 只要生产了一个,就通知消费者消费,解除阻塞
        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);
        usleep(100);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;
        // 判断是否有数据
        if(head != NULL) {
            // 有数据
            head = head->next;
            printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            usleep(100);
        } else {
            // 没有数据,需要等待
            // 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。
            pthread_cond_wait(&cond, &mutex);
            pthread_mutex_unlock(&mutex);
        }
    }
    return  NULL;
}

int main() {

    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    //实现线程分离
    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    while(1) {
        sleep(10);
    }

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    pthread_exit(NULL);

    return 0;
}


在这里插入图片描述说明:
1、消费线程进来,拿到互斥锁。
2、如果内容为空,则阻塞,同时释放互斥锁(这就完成了拿去和释放动作),无论有几个消费线程抢到锁,都会阻塞在这里。
3、生产线程抢到锁,于是生产。只要生产了就会通知消费线程。同时下一步解除锁。
4、!!!!== wait解除阻塞,但是同时也重新拿到了锁!!!==很重要!!!
5、此时再次评估条件,满足条件,拿着锁向下执行

void* costom(void* arg){
    while(1){
        pthread_mutex_lock(&mutex);
        struct Node* tmp = head;
        if(head != NULL) {
            head = head->next;
            printf("delete node ,num : %d,tid:%ld\n",tmp->num,pthread_self());
            free(tmp);
            pthread_mutex_unlock(&mutex);
            usleep(100);
        }else{
            //没有数据 需要等待
            // // 当这个函数调用阻塞的时候,会对互斥锁进行解锁,当不阻塞的,继续向下执行,会重新加锁。
            pthread_cond_wait(&cond,&mutex);
            //!!!!!!!!!!所以这一步必不可少
            pthread_mutex_unlock(&mutex);
            //收到信号,回到这里,生产者一释放锁,wait就重新拿到锁。但拿到锁以后,出else,到while入口还有一步拿锁,此时的lock无法执行,就会阻塞在那里。所以一定要在wait后面释放锁。
        }
    }

    return NULL;
}

3.14信号量(semaphore)

信号量,信号灯,灯亮则表示资源可用,灯灭则表示资源不可用。信号量主要用于阻塞线程,但不保证线程安全。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

生产者信号量初始化为8,每调用一次sem_wait(&psem),生产者信号量-1,每生产一个,消费者信号量加一,当生产者信号量为0时,阻塞。
消费者部分:每调用一次sem_wait(&csem),消费者信号量-1,每消费一个,生产者信号量加一,当消费者信号量为0时,阻塞。

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

// 创建一个互斥量
pthread_mutex_t mutex;
// 创建两个信号量
sem_t psem;
sem_t csem;

struct Node{
    int num;
    struct Node *next;
};

// 头结点
struct Node * head = NULL;

void * producer(void * arg) {

    // 不断的创建新的节点,添加到链表中
    while(1) {
        sem_wait(&psem);
        pthread_mutex_lock(&mutex);
        struct Node * newNode = (struct Node *)malloc(sizeof(struct Node));
        newNode->next = head;
        head = newNode;
        newNode->num = rand() % 1000;
        printf("add node, num : %d, tid : %ld\n", newNode->num, pthread_self());
        pthread_mutex_unlock(&mutex);
        sem_post(&csem);
    }

    return NULL;
}

void * customer(void * arg) {

    while(1) {
        sem_wait(&csem);
        pthread_mutex_lock(&mutex);
        // 保存头结点的指针
        struct Node * tmp = head;
        head = head->next;
        printf("del node, num : %d, tid : %ld\n", tmp->num, pthread_self());
        free(tmp);
        pthread_mutex_unlock(&mutex);
        sem_post(&psem);
       
    }
    return  NULL;
}

int main() {

    pthread_mutex_init(&mutex, NULL);
    sem_init(&psem, 0, 8);
    sem_init(&csem, 0, 0);

    // 创建5个生产者线程,和5个消费者线程
    pthread_t ptids[5], ctids[5];

    for(int i = 0; i < 5; i++) {
        pthread_create(&ptids[i], NULL, producer, NULL);
        pthread_create(&ctids[i], NULL, customer, NULL);
    }

    for(int i = 0; i < 5; i++) {
        pthread_detach(ptids[i]);
        pthread_detach(ctids[i]);
    }

    while(1) {
        sleep(10);
    }

    pthread_mutex_destroy(&mutex);

    pthread_exit(NULL);

    return 0;
}

C++实现生产者消费者模型

参考佬博客如下:
https://blog.csdn.net/qq_42214953/article/details/104878720
在这里插入图片描述

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

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

相关文章

供水管网漏损监测,24小时保障城市供水安全

供水管网作为城市生命线重要组成部分&#xff0c;其安全运行是城市建设和人民生活的基本保障。随着我国社会经济的快速发展和城市化进程的加快&#xff0c;城市供水管网的建设规模日益增长。然而&#xff0c;由于管网老化、外力破坏和不当维护等因素导致的供水管网漏损&#xf…

RabbitMQ的安装

RabbitMQ的安装 1、Windows环境下的RabbitMQ安装步骤 使用的版本&#xff1a;otp_win64_23.2 rabbitmq-server-3.8.16 版本说明&#xff1a;https://www.rabbitmq.com/which-erlang.html#compatibility-matrix 1.1 下载并安装erlang RabbitMQ 服务端代码是使用并发式语言…

8.5作业

要求实现AB进程对话 a.A进程先发送一句话给B进程&#xff0c;B进程接收后打印 b.B进程再回复一句话给A进程&#xff0c;A进程接收后打印 c.重复1.2步骤&#xff0c;当收到quit后&#xff0c;要结束AB进程 A进程 #include<stdio.h> #include<string.h> #include&…

Linux文本三剑客---grep、sed、awk

目录标题 1、grep1.1 命令格式1.2命令功能1.3命令参数1.4grep实战演练 2、sed2.1 认识sed2.2命令格式2.3常用选项options2.4地址定界2.5 编辑命令command2.6用法演示2.6.1常用选项options演示2.6.2地址界定演示2.6.3编辑命令command演示 3、awk3.1认识awk3.2常用命令选项3.3awk…

中国中医中药元宇宙 中药材价格缘何“狂飙”

◇相比去年同期&#xff0c;有超200个常规品种涨幅高于50%&#xff0c;25个常用大宗药材涨幅超200%&#xff0c;个别品种甚至涨价4至9倍 ◇在中药材价格普遍高涨的情况下&#xff0c;部分市场仓库库存数量也较多&#xff0c;出现囤积居奇倾向 ◇“不少游资和热钱涌入中药材市场…

MyBatis查询数据库之一(概念+创建项目+基础交互)

目录 1.MyBatis是什么&#xff1f; 2.为什么学习MyBatis&#xff1f; 3. 怎么学 MyBatis 4.第⼀个MyBatis查询 4.1 添加MyBatis框架支持 4.1.1老项目添加MyBatis 4.1.2 新项目添加MyBatis 4.2 配置连接字符串和MyBatis 4.2.1 配置连接字符串 4.2.2 配置 MyBatis 中的…

小白电脑装机(自用)

几个月前买了配件想自己装电脑&#xff0c;结果最后无法成功点亮&#xff0c;出现的问题是主板上的DebugLED黄灯常亮&#xff0c;即DRAM灯亮。对于微星主板的Debug灯&#xff0c;其含义这篇博文中有说明。 根据另一篇博文&#xff0c;有两种可能。 我这边曾将内存条和主板一块…

设计模式之模板方法

一、概述 定义一个操作中的算法的骨架&#xff0c;将一些步骤延迟到子类中。 TemplateMethod使得子类可以不改变一个算法的结构即可重新定义该算法的某些特定步骤。 二、适用性 1.一次性实现一个算法的不变的部分&#xff0c;并将可变的行为留给子类来实现。 2.各子类中公共…

数据库与数据仓库的区别及关系

数据库与数据仓库的区别及关系 数据库数据仓库异同差异联系例子 数据库 数据库是结构化信息或数据的有序集合&#xff0c;一般以电子形式存储在计算机系统中。通常由数据库管理系统 (DBMS) 来控制。它是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集…

vue v-slot指令

目录 定义语法使用场景场景一场景二场景三tips只有一个默认插槽时 定义 在Vue中&#xff0c; v-slot 指令用于定义插槽的模板内容。它用于在父组件中传递内容到子组件中的插槽。 v-slot 指令可以用于 标签或组件标签上&#xff0c;以便在子组件中使用插槽。 语法 使用 v-slo…

记录--基于css3写出的流光登录(注释超详细!)

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 完整效果 对基本的表单样式进行设置 这里设置了基本的表单样式&#xff0c;外层用了div进行包裹&#xff0c;重点是运用了两个i元素在后期通过css样式勾画出一条线没在聚焦文本框的时候线会过度成一个…

【Spring框架】Spring事务

目录 Spring中事务的实现编程式事务声明式事务Transactional 作⽤范围Transactional 参数说明注意事项Transactional ⼯作原理 MySQL 事务隔离级别Spring 事务隔离级别事务传播机制 Spring中事务的实现 Spring中事务操作分为两类&#xff1a; 1.编程式事务 2.声明式事务 编程…

c语言小知识点

文章目录 int main()与int main(void)符号常量常变量无符号赋值将占字节多的赋值给字节少的类型赋初值 表达式预处理格式符e格式符 循环for 输入长度相关输出文件管理 int main()与int main(void) int main(void) 指的是此函数的参数为空&#xff0c;不能传入参数&#xff0c;…

Streamlit的第一个应用(二)

文章目录 1 前言2 创建第一个应用 &#x1f4dd;&#x1f680;3 获取数据 &#x1f4e6;&#x1f50d;4 函数缓存&#x1f680;&#x1f50d;&#x1f4ca;5 赏析原始数据 ✨&#x1f389;6 绘制直方图 &#x1f4ca;✨7 所有乘车点的地图 &#x1f30d;&#x1f696;8 完整代码…

Linux修改系统语言

sudo dpkg-reconfigure locales 按pagedown键&#xff0c;移动红色光标到 zh_CN.UTF-8 UTF-8&#xff0c;空格标记*号&#xff08;没标记下一页没有这一项&#xff09;&#xff0c;回车。 下一页选择 zh_CN.UTF-8。 如果找不到 dpkg-reconfigure whereis dpkg-reconfigure …

手机变电脑2023之虚拟电脑droidvm

手机这么大的内存&#xff0c;装个app来模拟linux&#xff0c;还是没问题的。 app 装好后&#xff0c;手指点几下确定按钮&#xff0c;等几分钟就能把linux桌面环境安装好。 不需要敲指令&#xff0c; 不需要对手机刷机&#xff0c; 不需要特殊权限&#xff0c; 不需要找驱…

第二十二章 原理篇:UP-DETR

最近一直在忙各种各样的面试&#xff0c;顺便重新刷了一遍西瓜书。 感觉自己快八股成精了&#xff0c;但是一到写代码的环节就拉跨&#xff0c;人真是麻了。 许愿搬家前可以拿到offer&#xff01; 参考教程&#xff1a; https://arxiv.org/pdf/2011.09094.pdf https://zhuanla…

第一百二十二天学习记录:C++提高:STL-vector容器(上)(黑马教学视频)

vector基本概念 功能&#xff1a; vector数据结构和数组非常相似&#xff0c;也称为单端数组 vector与普通数组区别&#xff1a; 不同之处在于数组是静态空间&#xff0c;而vector可以动态扩展 动态扩展&#xff1a; 并不是在原空间之后续接新的空间&#xff0c;而是找更大的内…

荐读 | 《揭秘云计算与大数据》

当我们回顾过去几十年的科技进步时&#xff0c;云计算和大数据在现代科技发展史上无疑具有里程碑式的意义&#xff0c;它们不仅改变了我们的生活方式&#xff0c;而且对各行各业产生了深远的影响。 在这个数字化时代&#xff0c;云计算和大数据技术已经成为推动全球发展的关键…

Java:如何破坏类加载器的双亲委派机制?

本文重点 我们前面分析过loadClass方法,我们可以发现,这个方法的逻辑就是双亲委派机制,也就是说只要不破坏这个方法,那么就不会破坏双亲委派机制。如果要想破坏双亲委派机制,我们需要在类中重写loadClass方法,只要这样,那么就不会走双亲委派机制了。 破坏还是不破坏双…