信号量(记录型,AND,信号量集)
1.记录型信号量:为了解决整形信号量让权等待的问题,添加一个阻塞队列,记录型信号量完全符合进程同步准则
(注意阻塞是进程主动的),当进程资源不够时,进程/线程进入阻塞队列
程序计数器定位在wait之后:这句话的意思是,记录型信号量的p操作,总是先预先分配资源,当进程/线程资源满足时,从阻塞队列进入就绪队列调度
执行时,不需要在重新分配资源了,因为提前预先分配了.当进程出来直接运行不用在分配资源了
注意:这里wait和signal中参数不是int类型的,是semaphore类型的,我也不知道,为啥都写成int类型的,教科书上都这样写,具体编程时要注意.
2.记录型信号量典型应用:注意,这里block,wake在linux中都没有,这里只是理论上的一个函数名字,下面我会讲linux下的p,v操作函数
3.Linux下的pv操作
补充:
sem_t 是一种数据类型,用来定义记录型信号量的,内部含有阻塞队列
sem_init函数是用来初始化记录型信号量的
eg:
sem_t a;
sem_init(&a,0,1); //初始化一个只有一个资源的记录型信号量
wait相当于linux下的sem_wait,signal相当于linux下的sem_post
信号量使用完,必须销毁,在主线程中使用sem_destroy函数销毁
eg:
seg_destroy(&a);
4.实例:
这个策略,什么信号量都适合使用,包括后面的AND信号量和信号量集,都是基于这个原理
题目说明:盘子中一直只能有一个水果,实现同步
题目分析:我们采用记录型信号量解决这个问题
1.在问题域中找临界资源,
发现爸爸,妈妈关注盘子资源是不是(为空)能放水果,所以盘子(plate)是临界资源
女儿只关心能不能吃到苹果(apple),所以对女儿来说苹果是一个临界资源
儿子只关心能不能吃到橘子(orange),所以对于儿子来说橘子就是一个临界资源
所以临界资源有:plate,apple,orange
2.为每一个临界资源定义一个信号量
plate:盘子,初始时,只有1个
apple:苹果,初始时,有0个
orange:橘子,初始时,有0个
3,4步在具体编程中使用,切记切记,一定遵循这个原则
分析,父亲母亲(wait盘子,signal水果),占用盘子资源放水果,
当儿子或女儿吃完水果时(wait水果,signal盘子),应该由儿子或女儿释放盘子资源
综上,可是现题目中的同步
源代码:
//m4.cpp 创建方法:vim m4.cpp 编译方法:g++ m4.cpp -lpthread -o m4 执行方法: ./m4
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
sem_t plate,orange,apple;//定义三个信号量
//void* 表示返回值可以是任意类型的指针, 形参中的void *p表示传入的形参可以是任意类型的指针,可用于传递多个参数
void *father(void *p) //父亲函数,用于创建线程
{
while(1)
{
sem_wait(&plate); //3,4原则,父亲线程先判断是不是有盘子资源
printf("the father put a apple\n");
sem_post(&apple); //父亲,放完苹果,盘子中苹果数目+1=1,如果你这里有疑问请看最上面signal函数的伪代码逻辑
}
return NULL;
}
void *mother(void *p) //同理
{
while(1)
{
sem_wait(&plate);
printf("the mother put a orange\n");
sem_post(&orange);
}
return NULL;
}
void *daughter(void *p)
{
while(1)
{
sem_wait(&apple); //女儿判断盘子中是不是有苹果
printf("the daughter eats a apple\n");
sem_post(&plate); //女儿,吃完苹果后,应该释放盘子资源,为了父亲,母亲能在放水果
}
return NULL;
}
void *son(void *p)
{
while(1)
{
sem_wait(&orange);
printf("the son eats a orange\n");
sem_post(&plate);
}
return NULL;
}
int main()
{
sem_init(&plate,0,1);//初始化盘子临界资源,初始1个
sem_init(&orange,0,0);//初始化橘子临界资源,初始0个
sem_init(&apple,0,0);//初始化苹果临界资源,初始0个
pthread_t tid[4]; //创建四个进程
pthread_create(&tid[0],NULL,&father,NULL);
pthread_create(&tid[1],NULL,&mother,NULL);
pthread_create(&tid[2],NULL,&daughter,NULL);
pthread_create(&tid[3],NULL,&son,NULL);
pthread_join(tid[0],NULL); //主线程阻塞,等待子进程(线程执行),主线程不阻塞的话,会直接执行完
sem_destroy(&plate); //销毁信号量
sem_destroy(&apple);
sem_destroy(&orange);
return 0;
}
执行结果:死循环
5.AND型信号量和信号量集
进程/线程同步准则:这里不区分进程/线程,因为,引入了线程后线程就是资源调度的基本单位了,所以同步也可以指进程的线程同步
1.空闲让进:
2.忙则等待:
3.有限等待
4.让权等待:优先权低的进程让优先权高的的进程先执行
AND型信号量:可以避免死锁
为了解决一次分配多中资源,每种资源每次分配一个,一次获得进程所需要的所有资源(每种个1个),否则进程阻塞,是记录型信号量上的进一步延伸
AND型信号量的阻塞队列机制,为每种资源设置一个阻塞队列,当最先出现资源不足的资源种类为Ri时,那么进程就被阻塞在Ri资源对应的阻塞队列中
信号量集:申请n类资源,每类资源最低ti个,每类申请di个资源
AND的进一步延伸,设置一个最低资源数目>=1,和进程需要的资源数目>=0
6.记录型信号量和信号量集比较