LiteOS同步实验(实现生产者-消费者问题)

news/2025/2/12 5:13:33/

效果如下图:

给大家解释一下上述效果:在左侧(顶格)的是生产者(Producer);在右侧(空格)的是消费者(Consumer)。生产者有1个,代号为“0”;消费者有2个,代号分别为“0”和“1”。

生产者首先生产出一个产品,输出“is producing Product”。然后唤醒消费者来消费,输出“is waking Consumer”。

消费者生成时会报告自己的信息,比如“I am Consumer 0”代表它是0号消费者。如果有东西可以消费,它会输出“Consumer 代号 consume product success!!!!”代表消费成功。

程序实现的效果是:生产者不断生产“产品”,然后消费者“0”和“1”不断进行消费,如此循环往复。

一、代码剖析

代码逻辑如下,很清晰:

入口函数代码逻辑:

从HxSyscall进入,调用pthread_mutex_init初始化互斥锁mutex(是一个pthread_mutex_t结构体),调用pthread_cond_init初始化条件变量has_product(是一个pthread_cond_t结构体)。

紧接着定义了一个thread_ids数组,该数组用于存储生产者和消费者的编号。

然后先调用pthread_create函数生成生产者,在这里解释一下4个传入参数各自的含义:

第1个参数是一个指向pthread_t类型变量的指针,用于存储新创建的线程,简言之就是新创建的线程会存放在该变量里。

第2个参数是一个指向pthread_attr_t类型变量的指针,用于指定新线程的属性,这里是NULL,说明用的是默认属性。

第3个参数:是一个指向函数的指针,该函数就是新线程的入口点。可以这么理解:这里传入什么函数名就去调用哪个函数。比如在这里我们将生产者producer的函数名传入,就会去执行producer函数。

第4个参数:一个指向void类型的指针,用于传递给新线程入口点函数的参数。可以这么理解:这里传入的值就是要调用函数的参数,比如我们调用producer函数需要传入了一个参数arg,这里的这第4个参数就是这个参数arg。

大家需要注意一点:如果需要在线程执行时输出语句printf(),一定要在printf()后加上fflush(stdout);这条语句,因为当我们使用printf等输出函数打印文本时,并不会立即将其发送到屏幕上,而是先存储在输出缓冲区中,而fflush(stdout)这条语句可以刷新输出缓冲区,将缓冲区内容输出到屏幕上。

最后说一下pthread_join函数的是用于阻塞调用线程,直到指定的线程结束执行,实现线程的同步,确保调用线程等待指定线程的完成。在入口函数中执行该操作,会等待所有线程都执行完之后再继续执行。简而言之就是要避免:某些线程还在运行,却意外销毁了该些线程所需的资源,导致出错的情况发生。

补充:

1.互斥锁(Mutex)的作用是保护共享资源,确保同一时间只有一个线程可以访问共享资源。

2.条件变量(Condition Variable)的作用是实现线程间的协调和通信。条件变量允许一个线程等待某个条件的发生,并在条件满足时被唤醒。

 消费者函数逻辑:

用pthread_mutex_lock来上互斥锁,pthread_mutex_unlock来解除互斥锁,在此不过多赘述。

当ready==0时说明此时消费者生产的东西都被消费完了,或者还没来得及生产,因此用while循环来忙等待。

pthread_cond_wait函数的作用是使线程等待某个条件的发生,一旦条件满足(收到信号),线程将被唤醒并继续执行。在这里等待的是has_product变量。

当ready>0时,令ready-1表示消费掉1个产品,然后休眠3秒

生产者函数逻辑:

用pthread_mutex_lock来上互斥锁,pthread_mutex_unlock来解除互斥锁,在此不过多赘述。

pthread_cond_signal用于向线程发送信号,告诉它们条件已经满足,你可以执行啦!与消费者中的pthread_cond_wait函数相呼应。


 

二、可运行代码

代码如下可直接复制:

#include <stdlib.h>                                                      
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <pthread.h>#define CONSUMER_NUM 2#define PRODUCER_NUM 1pthread_t pids[CONSUMER_NUM+PRODUCER_NUM];int ready = 0;
int running =1;pthread_mutex_t mutex;pthread_cond_t has_product;void* producer(void* arg){int no = (int)arg;while(running){pthread_mutex_lock(&mutex);ready++;printf("Producer %d is producing Product\n",no);fflush(stdout); pthread_cond_signal(&has_product);printf("Producer %d is waking Consumer\n",no);fflush(stdout); pthread_mutex_unlock(&mutex);sleep(1);}
return NULL;
}void* consumer(void* arg){int num = (int)arg;while(running){pthread_mutex_lock(&mutex); while(ready==0){printf("\tConsumer %d is waiting...\n",num);fflush(stdout); pthread_cond_wait(&has_product,&mutex);}ready--;printf("\tConsumer %d consume product success!!!!!\n",num);fflush(stdout); pthread_mutex_unlock(&mutex);sleep(3);}
return NULL;
}void HxSyscall(int num){                                          pthread_mutex_init(&mutex,NULL);pthread_cond_init(&has_product,NULL);printf("init success!\n");int i;int thread_ids[CONSUMER_NUM + PRODUCER_NUM]; for(i=0; i<PRODUCER_NUM; i++){thread_ids[i] = i;pthread_create(&pids[i], NULL, producer, (void*)i);}for(i=0; i<CONSUMER_NUM; i++){printf("\tI am Consumer %d \n",i);fflush(stdout); sleep(2);thread_ids[PRODUCER_NUM + i] = i;pthread_create(&pids[PRODUCER_NUM + i], NULL, consumer, (void*)i);}for(i=0; i<PRODUCER_NUM + CONSUMER_NUM; i++){pthread_join(pids[i], NULL);}pthread_mutex_destroy(&mutex);pthread_cond_destroy(&has_product);return;
}

大家只需要按照project1的方式,将上述代码放入home/openharmony/kernel/liteos_a/syscall下的hx_syscall.c文件夹下即可(这里为了方便基础较薄弱的同学操作,所以我们仍旧采用勖哥在pro1中的函数命名),接下来大家只需要按照pro1的方式进行编译烧录即可运行。

【如果觉得有帮助记得点赞+收藏​​​​​​⭐】


http://www.ppmy.cn/news/1233207.html

相关文章

如何在 ASP.NET Core 中使用 Quartz.NET

当我们在web开发中&#xff0c;常常会遇到这么一个需求&#xff0c;在后台执行某一项具体的任务&#xff0c;具体的说就是这些任务必须在后台定时执行。 Quartz.NET 是一个开源的 JAVA 移植版&#xff0c;它有着悠久的历史并且提供了强大的 Cron 表达式&#xff0c;这篇我们就…

【python学习】基础篇-常用模块-Base64:用64个字符表示任意二进制数据的方法,常用于在URL、Cookie、网页中传输少量二进制数据

Base64是一种用64个字符表示任意二进制数据的方法&#xff0c;常用于在URL、Cookie、网页中传输少量二进制数据。 Python中的base64模块提供了一些方法用于编码和解码Base64字符串。 以下是一些常用的base64用法&#xff1a; 1.编码&#xff1a;将二进制数据转换为Base64字符…

万字解析设计模式之 适配器模式

一、 适配器模式 1.1概述 将一个接口转换成客户希望的另一个接口&#xff0c;适配器模式使接口不兼容的那些类可以一起工作。 适配器模式分为类适配器模式和对象适配器模式&#xff0c;前者类之间的耦合度比后者高&#xff0c;且要求程序员了解现有组件库中的相关组件的内部结…

python3.7升级为更高版本并迁移库

创建虚拟环境 # 在进入当前的虚拟环境【py3.7的环境】使用pip导出全部包txt文件 pip freeze > all_package.txt# 创建虚拟环境 conda create -n py39 python3.9# 激活新创建的虚拟环境 conda activate py39# 用 pip 一键文件安装 # pip install --help 查看-r命令的作用 # …

JAVA整理学习实例(四)数据结构

JAVA整理学习实例&#xff08;四&#xff09;数据结构 注&#xff1a;不积跬步&#xff0c;无以至千里&#xff0c;学习之路&#xff0c;任重而道远。很多技术&#xff0c;学着学着就回到了理论上。基础知识很差&#xff0c;博客写起来很难。。。写的不对的和不好的&#xff0c…

PaddleDetection训练目标检测模型

PaddleDetection训练目标检测模型 一&#xff0c;安装标注软件二&#xff0c;数据标注和清洗三&#xff0c;安装PaddleDetection环境四&#xff0c;修改配置文件&#xff0c;本文选择的是 PP-PicoDet算法五&#xff0c;训练模型六&#xff0c;训练完成之后导出模型七&#xff0…

uniapp开发断小程序-如何判断小程序是在手机端还是pc端打开

官方说明 https://developers.weixin.qq.com/miniprogram/dev/devtools/pc-dev.html 小程序如何判断是 PC 平台&#xff1f; 通过 getSystemInfo 官方接口&#xff08;platform 是 windows&#xff09; 通过 UA&#xff08;PC UA 包含 MiniProgramEnv/Windows&#xff09; …

webGL技术开发的软件类型

WebGL 是一种在浏览器中渲染 2D 和 3D 图形的 JavaScript API。通过 WebGL&#xff0c;你可以创建各种类型的软件项目&#xff0c;特别是那些需要强大图形渲染能力的项目。以下是一些你可以使用 WebGL 实现的软件项目类型&#xff0c;希望对大家有所帮助。北京木奇移动技术有限…