linux线程pv操作单缓冲区的生产者消费者问题,Linux实现PV操作生产者输入字符消费者打印字符...

题目为:假定有一个生产者和一个消费者,生产者每次生产一件产品,并且把生产的产品存入共享缓冲区以供消费者取走使用。

消费者每次从缓冲区内取出的一件产品去消费。禁止生产者将产品放入已满的缓冲器内,禁止消费者从缓冲器内取产品。假定缓冲

区可同时存放10件产品,用PV操作实现消费者与生产者的同步问题。

这是一道很常见的同步问题,我用信号量来实现。

信号量是用于多个进程(线程)对共享数据的访问的计数器:

1.当信号量的值为正时,则进程(线程)可以使用该资源。在这种情况下,进程(线程)会将信号量减1,表示它使用了一个资源单位。

2.当信号量为0,则进程进入休眠状态,直到信号量大于0。进程被唤醒后,它返回步骤1。

常用信号量形式被称作二元信号量。它控制单个资源,其初始值为1。但是信号量可以是任意一个正值,该值表示有多个共享着资源单位可共享应用。

unix提供了两种信号量,XSI和POSIX信号量。

在 本例子中用POSIX信号量实现。

sem_wait函数:实现信号量减1的操作,如果信号量计数是0就会发生阻塞。直到成功使信号量减1或者被信号中断才返回。

sem_post函数:实现信号量增1的操作,在调用sem_post时,如果在调用sem_wait中发生进程阻塞。那么进程会被唤醒并且被sem_post增加1的信号量计数会再次被sem_wait减1。

信号量分为命名信号量和未命名信号量。

在使用POSIX调用sem_init函数来创建一个未命名的信号量和sem_destory丢弃使用完的信号量。

首先创建一个信号量sem_mutex表示对缓冲区的操作,用sem_init(&sem_mutex,0,1)初始化。0表示使用得是线程,1表示只有一个共享资源。

每次对缓冲区进行操作时,就需要调用sem_wait(&sem_mutex)相当于P操作,表示正在操作缓冲区,由于初始值为1,所以其他线程想对其操作时,必须等待此操作调用sem_post相对于V操作,表示对其操作结束,另一个线程可以进入对其操作。这样就可以实现数据的同步,避免出现与"时间相关的错误"。

由于生产者和消费者分别用一个线程执行操作,由于线程时可以共享数据的。

定义一个结构体表示缓冲队列的资源,队列里面可以放10个字符,num表示已经放入缓冲区的数量。

当生产者执行时num++,消费者执行时num--。

rear,front便于存入和打印出字符

信号量s1和s2分别表示对缓冲区空位置的操作和表示缓冲区已经放入的产品的操作

s1和s2的初始值分别为10和1

当s1小于10时表示生产者继续向缓冲区的空位置放入产品的操作

当s2大于0时表示消费者可以从缓冲区里面继续取产品操作

#include

#include

#include

#include

#include

sem_t sem_mutex;//表示对缓冲区的操作

sem_t s1;//信号量s1表示缓冲区区还可以放入的产品

sem_t s2;//信号量s2表示缓冲区已经放的产品

#define BUFF_SIZE 10

struct P_Queue{

char products[BUFF_SIZE];

int num;

int front,rear;

};

void* Producer(void *arg)

{

struct P_Queue *q;

q = (struct P_Queue *)arg;

while(1)

{

sem_wait(&sem_mutex);

if(q->num

{

sem_wait(&s1);

char c1;//输入的字符

char c2;//回车键

printf("input\n");

scanf("%c",&c1);

c2 = getchar();

q->rear = (q->rear+1)%BUFF_SIZE;

q->products[q->rear]=c1;

printf("Products: %c\n",c1);

q->num++;

printf("Buffer Size:%d\n",q->num);

sem_post(&s2);

}

sem_post(&sem_mutex);

sleep(rand()%2);

}

return NULL;

}

void* Consumer(void *arg)

{

struct P_Queue *q;

q = (struct P_Queue*)arg;

while(1)

{

sem_wait(&sem_mutex);

if(q->num>0)

{

sem_wait(&s2);

q->front = (q->front+1)%BUFF_SIZE;

char c = q->products[q->front];

q->num--;

printf("Consumer show content: %c\n",c);

printf("Buffer Size: %d\n",q->num);

sem_post(&s1);

}

sem_post(&sem_mutex);

sleep(rand()%2);

}

}

int main()

{

struct P_Queue *q;

q = (struct P_Queue *)malloc(sizeof(struct P_Queue));

q->front = q->rear = 0;

q->num =0;

pthread_t pid1,cid1;

sem_init(&s1,0,BUFF_SIZE);

sem_init(&s2,0,0);

sem_init(&sem_mutex,0,1);

pthread_create(&pid1,NULL,Producer,(void*)q);

pthread_create(&cid1,NULL,Consumer,(void*)q);

pthread_join(pid1,NULL);

pthread_join(cid1,NULL);

printf("hello");

sem_destory(&s1);

sem_destory(&s2);

sem_destory(&sem_mutex);

return 0;

}

效果:

0818b9ca8b590ca3270a3433284dd417.png

由于线程调度是由操作系统控制的,所以并不是执行一次生产者,就接着执行一次消费者。

注意运行的时候应该加 -lpthread,不然会找不到sem_wait,sem_post,sem_init等函数

0818b9ca8b590ca3270a3433284dd417.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值