P、V操作原语定义:
- P原语操作: 信号量减1,若此时信号仍然大于等于0,则进程继续执行; 若此时信号量小于零,则进程阻塞,在队列中排队,然后等待。
- V原语操作:信号量加1,若此时信号量大于零,则进程继续执行,若此时信号量小于或等于零,则唤醒阻塞在此信号量的进程,然后执行该V操作的进程继续执行。
生产者消费者问题
- 在缓冲区为空时,消费者不能再进行消费
- 在缓冲区为满时,生产者不能再进行生产
- 在一个线程进行生产或消费时,其余线程不能再进行生产或消费等操作
- 即保持线程间的同步 注意条件变量与互斥锁的顺序
伪代码描述
item B[k];
semphore empty;
empty=k;
semphore full;
full=0;
semphore mutex;
metax=1;
int in=0; //写指针
int out=0;//读指针
cobegin//并发结束
process producer_i( ) process consumer_j( )
{ {
while(true) while(true)
{ {
produce();//生产数据 p(full);
p(empty); p(mutex);
p(mutex); take from B[out];
append to B[in];//将数据放入缓冲区 out=(out+1)%k //从缓冲区中取出数据
in=(in+1)%k v(mutex);
v(mutex); v(empty);
v(full); consume();//消费数据
} }
} }
coend//并发开始
c语言代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#define Maxbuf 10
#define TimesOfOp 10
#define true 1
#define false 0
//定义循环缓冲队列及其操作
struct Circlebuf
{
int read;
int write;
int buf[Maxbuf];
}circlebuf;
sem_t mutex;
sem_t empty;
sem_t full;
int product_id = 0; //生产者id
int prochase_id = 0; //消费者id
void writeCirclebuf(struct Circlebuf *circlebuf,int *value)
//向缓冲区中写一个值
{
circlebuf->buf[circlebuf->write]=(*value);
circlebuf->write=(circlebuf->write+1)%Maxbuf;
}
int readCirclebuf(struct Circlebuf *circlebuf)
{
int value=0;
value=circlebuf->buf[circlebuf->read];
circlebuf->buf[circlebuf->read]=0;
circlebuf->read=(circlebuf->read+1)%Maxbuf;
return value;
}
void OutCirclebuf(struct Circlebuf *circlebuf)
{
int i;
printf("现存:");
for(i=0;i<Maxbuf;i++)
{
printf("%d ",circlebuf->buf[i]);
}
printf("\n");
}
void sigend(int sig)
{
exit(0);
}
void *productThread(void *i)
{
int id=product_id++;
int *n=(int *)i;
int t=TimesOfOp;
while(t--)
{
sem_wait(&empty);
sem_wait(&mutex);
writeCirclebuf(&circlebuf,n);
printf("生产者 %d 写入= %d. \n",id,*n);
OutCirclebuf(&circlebuf);
sem_post(&mutex);
sem_post(&full);
}
}
void *consumerThread(void *i)
{
int id=prochase_id++;
int *n=(int *)i;
int value=0;
int t=TimesOfOp;
while(t--)
{
sem_wait(&full);
sem_wait(&mutex);
value=readCirclebuf(&circlebuf);
printf("消费者 %d 取走= %d. \n",id,*n);
OutCirclebuf(&circlebuf);
sem_post(&mutex);
sem_post(&empty);
}
}
int main()
{
int i;
int ConsNum=0,ProdNum=0,ret;
pthread_t cpid,ppid;
sem_init(&mutex,0,1);
sem_init(&empty,0,Maxbuf);
sem_init(&full,0,0);
signal(SIGINT,sigend);
signal(SIGTERM,sigend);
//初始化循环缓冲队列
circlebuf.read=circlebuf.write=0;
for(i=0;i<Maxbuf;i++)
{
circlebuf.buf[i]=0;
}
printf("输入生产者的数目:");
scanf("%d",&ProdNum);
int *pro=(int *)malloc(ProdNum*sizeof(int));
printf("输入消费者的数目:");
scanf("%d",&ConsNum);
int *con=(int *)malloc(ProdNum*sizeof(int));
for(i=1;i<ConsNum;i++)
{
cpid=i;
con[i-1]=i;
ret=pthread_create(&cpid,NULL,consumerThread,(void *)&con[i-1]);
if(ret!=0)
{
printf("Create thread error");
exit(1);
}
}
for(i=1;i<=ProdNum;i++)
{
ppid=i+100;
pro[i-1]=i;
ret=pthread_create(&ppid,NULL,productThread,(void *)&pro[i-1]);
if(ret!=0)
{
printf("Create thread error");
exit(1);
}
}
sleep(1);
sem_destroy(&mutex);
sem_destroy(&empty);
sem_destroy(&full);
pthread_exit(NULL);
}
读者写者问题
- 允许多个读者可以同时对文件执行读操作;
- 只允许一个写者往文件中写信息;
- 任一写者在完成写操作之前不允许其他读者或写者工作;
- 写者执行写操作前,应让已有的读者和写者全部退出。
- 也就是说,读进程不排斥其他读进程,而写进程需要排斥其他所有进程,包括读进程和写进程。
伪代码
int readcount;
semphore wirteblock,mutex;
wirte=1;
mutex=1;
readcount=0;
cobegin
process reader( ) process wirter()
{ {
P(mutex) P(wirteblock)
readcount++; {写操作}
//如果是第一个读者,p(wirteblock),防止写者进行写操作 v(wirteblock)
if(readcount==1) p(wirteblock) V(wirteblock)
V(mutex) }
{读操作}
P(mutex)
readcount--;
//如果是最后一个读者,v(wirteblock),允许写者进行写操作
if(readcount==0) v(writeblock)
V(mutex)
}
coend
睡眠理发师问题
- 有一个理发师的椅子,和n个顾客的椅子 如果有顾客在椅子上等,那么理发师为他剪发,否则理发师就在自己的椅子上睡觉。
- 如果理发师在熟睡,那么顾客会叫醒理发师,否则顾客会看有没有空椅子,有的话,他坐下等,否则,他将离开理发店。
伪代码
semphore mutex,consumer,barber;
int chair,waiting;
mutex=1;
consumer=0;
barber=1;
chair=N;
waiting=0;
cobegin
process barber()
{
while(true)
{
P(consumer);
P(mutex);
waiting--;
V(barber);
V(mutex);
cut_hair();
}
}
process consumer()
{
P(mutex)
if(waiting<chair)
{
waiting++;
V(consumer);
V(mutex);
P(barber);
get_haircut();
}
else V(mutex)
}
coend