信号量实现生产者消费者模型

一、信号量

信号量是进化版的互斥锁,由于互斥锁的粒度比较大,如果我们希望在多个线程间对某一对象的部分数据进行共享,使用互斥锁是没有办法实现的,只能将整个数据对象锁住,这样虽然达到了多线程操作共享数据正确性的目的,却无形中导致线程的并发性下降。线程从并行执行,变成了串行执行。与直接使用单进程无异。而信号量是相对折中的一种处理方式,既能保证同步,数据不混乱又能提高线程的并发。

二、主要应用函数

sem_t sem; 定义信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数:
	sem: 信号量
	pshared: 0: 用于线程间同步
	  		  1: 用于进程间同步
	value:N 值(指定同时访问的线程数)	

int sem_destroy(sem_t *sem);

int sem_wait(sem_t *sem); 
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
	 一次调用,做一次-- 操作,当信号量的值为 0 时,再次 -- 就会阻塞。
	(对比 pthread_mutex_lock()int sem_post(sem_t *sem);
	一次调用,做一次++ 操作
   (对比 pthread_mutex_unlock())
   
返回值: 均是成功:0	失败:-1,errno被设置

三、生产者消费者模型

开发流程图示:

在这里插入图片描述

需要注意的是:使用信号量实现生产者消费者模型,对于生产过程消费过程并不是原子操作,单一生产者、消费者没什么问题,但是多生产者、消费者情况下,生产过程或消费过程过于耗时,不能保证不会出现生产冲突消费冲突的后果。

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

#define MAX     5

sem_t sem_pro;	//产品
sem_t sem_sto;	//容量
int queue[MAX];
int i, j;

void *producer(void* arg)
{
        while (1) {
                sem_wait(&sem_sto);	//容量--,容量为0则阻塞等待

                queue[j] = rand()%200 + 1;	//生产产品
                printf("%lu:Produce a product:%d\n", pthread_self(), queue[j]);

                sem_post(&sem_pro);	//产品数++

                j = (j+1) % MAX;	//借助下标实现环形队列
                sleep(rand() % 3);
        }

        pthread_exit(NULL);
}

void *consumer(void *arg)
{
        while (1) {
                sem_wait(&sem_pro);	//产品--,产品为0则阻塞等待

                printf("        %lu:Consume a product:%d\n", pthread_self(), queue[i]);
                queue[i] = 0;	//消费产品

                sem_post(&sem_sto);	//容量++

                i = (i+1) % MAX;	//借助下标实现环形队列
                sleep(rand()%3);
        }

        pthread_exit(NULL);
}


int main()
{
        int ret;
        pthread_t producerID;
        pthread_t consumerID;

        srand(time(NULL));
        sem_init(&sem_pro, 0, 0);
        sem_init(&sem_sto, 0, MAX);
        ret = pthread_create(&producerID, NULL, producer, NULL);
        if (ret != 0) {
                fprintf(stderr, "pthread_create:producer error  ret:%s\n", strerror(ret));
                exit(-1);
        }
        ret = pthread_create(&consumerID, NULL, consumer, NULL);
        if (ret != 0) {
                fprintf(stderr, "pthread_create:consumer error  ret:%s\n", strerror(ret));
                exit(-1);
        }

        pthread_join(producerID, NULL);
        pthread_join(consumerID, NULL);
        sem_destroy(&sem_pro);
        sem_destroy(&sem_sto);

        return 0;
}

运行结果:

140011622741760:Produce a product:164
	140011614349056:Consume a product:164
140011622741760:Produce a product:10
	140011614349056:Consume a product:10
140011622741760:Produce a product:137
140011622741760:Produce a product:167
140011622741760:Produce a product:118
	140011614349056:Consume a product:137
	140011614349056:Consume a product:167
140011622741760:Produce a product:43
	140011614349056:Consume a product:118
	140011614349056:Consume a product:43
140011622741760:Produce a product:21
	140011614349056:Consume a product:21
### 关于 LiteOS 中信号量实现生产者消费者模型 在 LiteOS 操作系统中,信号量是一种用于进程间同步和互斥访问共享资源的重要工具。通过信号量可以有效解决生产者消费者问题中的资源竞争问题。 以下是基于 LiteOS 的生产者消费者模型的完整代码示例: #### 1. 初始化信号量 LiteOS 提供了 `LOS_SemCreate` 函数来创建信号量[^2]。为了模拟缓冲区的状态,通常会初始化两个信号量: - **emptySem**:表示空闲槽的数量。 - **fullSem**:表示已填充槽的数量。 ```c #include "los_sem.h" #include "los_task.h" #define BUFFER_SIZE 5 UINT32 g_buffer[BUFFER_SIZE]; UINT32 g_in = 0, g_out = 0; UINT32 emptySemID, fullSemID; // 创建信号量函数 VOID CreateSemaphores(VOID) { UINT32 ret; // 创建 emptySem,初始值为缓冲区大小 ret = LOS_SemCreate(BUFFER_SIZE, &emptySemID); if (ret != LOS_OK) { printf("Failed to create empty semaphore\n"); } // 创建 fullSem,初始值为 0 ret = LOS_SemCreate(0, &fullSemID); if (ret != LOS_OK) { printf("Failed to create full semaphore\n"); } } ``` #### 2. 生产者任务 生产者的职责是向缓冲区添加数据,并在每次操作前后调整信号量状态。如果缓冲区已满,则等待有空位后再继续工作。 ```c VOID ProducerTask(VOID) { INT32 item = 0; // 假设生产的物品是一个简单的计数值 while (1) { // 等待有一个空闲位置 LOS_SemPend(emptySemID, LOS_WAIT_FOREVER); // 添加到缓冲区 g_buffer[g_in] = item++; g_in = (g_in + 1) % BUFFER_SIZE; printf("Produced: %d\n", item - 1); // 通知消费者缓冲区中有新项目 LOS_SemPost(fullSemID); // 模拟延迟 LOS_TaskDelay(10); // 单位为 Tick } } ``` #### 3. 消费者任务 消费者的职责是从缓冲区取出数据,并在每次操作前后调整信号量状态。如果没有可消费的数据,则等待直到有新的数据到来。 ```c VOID ConsumerTask(VOID) { while (1) { // 等待缓冲区中有数据 LOS_SemPend(fullSemID, LOS_WAIT_FOREVER); // 取出数据 INT32 consumedItem = g_buffer[g_out]; g_out = (g_out + 1) % BUFFER_SIZE; printf("Consumed: %d\n", consumedItem); // 通知生产者缓冲区有了空位 LOS_SemPost(emptySemID); // 模拟延迟 LOS_TaskDelay(15); // 单位为 Tick } } ``` #### 4. 主程序入口 在主程序中,先初始化信号量并启动生产者消费者线程。 ```c INT32 main(VOID) { TSK_INIT_PARAM_S producerParam = {0}; TSK_INIT_PARAM_S consumerParam = {0}; // 创建信号量 CreateSemaphores(); // 配置生产者任务参数 producerParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ProducerTask; producerParam.pcName = "Producer"; producerParam.uwStackSize = 0x800; producerParam.usTaskPrio = 10; // 配置消费者任务参数 consumerParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ConsumerTask; consumerParam.pcName = "Consumer"; consumerParam.uwStackSize = 0x800; consumerParam.usTaskPrio = 11; // 创建任务 LOS_TaskCreate(&producerParam); LOS_TaskCreate(&consumerParam); return 0; } ``` --- ### 注意事项 1. 如果尝试对已经满了的信号量执行 `LOS_SemPost` 操作,可能会触发错误码 `LOS_ERRNO_SEM_OVERFLOW`。 2. 同样地,当调用 `LOS_SemPend` 而信号量不可用时,可能返回错误码 `LOS_ERRNO_SEM_UNAVAILABLE`。 上述代码展示了如何利用 LiteOS 的信号量机制实现经典的生产者消费者模型,解决了多线程环境下的资源共享与同步问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值