目录
访问临界资源循环可以被描述为
while (TRUE)
{
进入区 //进入区和退出区是实现互斥的代码
临界区 //进程中访问临界资源的代码段
退出区
剩余区
}
信号量语句类型
整型信号量(S)
S是一个表示资源数目的整型变量
可以初始化:int S = 1;//可以比作系统中有一台打印机
//wait等价于: wait简称P signal简称V
wait(S)//相当于进入区
{
while(S<=0);//如果资源不够,就一直循环等待 所以会导致忙等(为遵循让权等待)
s--;//如果资源大于0 则消耗一个资源
}
//signal(S)等价于:
signal(S)//相当于退出区
{
S++;//如果退出则返还该资源
}
因为这两个操作都是原子操作(原子操作:一个操作中的所有动作要么全做要么全不做 是一个不可分割的基本单位 原子操作在系统态下执行 常驻内存)所以只要开始执行了wait或者signal就会一直持续到执行结束
记录型信号量(semaphore型变量)
因为他采用记录型数据结构而得名
相较于整型信号量记录型信号量增加了 如果该进程无法获取相应资源时可以选择挂起而不是一直忙等 暂时实现了让权等待 因为此时是多个进程仅仅共享一个临界资源
typedef struct{
int value; //资源信号量(资源的数量)
struct process_control_block *list; //等待对列
}semaphore;
/*请求一个单位的该类资源*/
void wait(semaphore *S){
S->value--;//该类资源减少一个
if(S->value <0){
block(S->list);//如果资源数量不够分配 则调用原语block进行自我阻塞 并且插入到信号量链表S->list中(让权等待)s->value的绝对值表示该信号量链表中已阻塞进程的数目
}
}
/*释放一个单位的该类资源*/
void signal(semphore S){
S->value++;//增加一个该类资源
if(S->value<=0){
wakeup(S->list);//如果+1之后 s->value的值仍然小于等于0 则表示该信号量中仍然有进程在等待该资源被阻塞 所以调用wakeup原语 将S->list链表中第一个等待进程唤醒
}
}
AND型信号量 Swait(s1,s2,…);Ssignal(S1,S2,…)
如果进程A和进程B都要访问临界资源C和D并且CD资源数量都只有一个 此时如果用记录型信号量
typedef struct{
int value; //资源信号量(资源的数量)
struct process_control_block *list; //等待对列
}semaphore;
semaphore *C,*D;
c->value = 1;
D->value = 1;
process A: process B:
wait(D); wait(C);
wait(C); wait(D);
//如果此时并发交替执行
process A:wait(D);//先执行A程序的wait(D)此时D->value变为0
process B:wait(C);//再执行B程序的wait(C)此时C->value变为0
process A:wait(C);//再执行A程序的wait(C)此时C->value变为-1 程序A阻塞
process B:wait(D);//最后执行B程序的wait(D)此时D->value变为-1 程序B阻塞
//这样就发生了死锁
所以为了解决这个问题在wait中增加了“AND”操作 也就是&& 写成Swait(S1,S2,S3…Sn) 也就是将进程在整个运行过程中需要的所有资源一次性全部分配给进程 等进程使用完后一起释放 只要有一个资源无法分配给进程 其他所有的资源都不分配给该进程 并且将该进程放入相应的等待队列中,并将此进程的程序计数设置为Swait操作的开始
Ssignal就是会释放所有相应的资源
信号量集(允许进程获取多个同类资源)
上面几种信号量都只能对每个资源每次申请一个单位资源 所有信号量集做出了改变 他可以对每个资源每次申请多个单位资源
Swait(S1,t1,d1,S2,t2,d2,…Sn,tn,dn)
对S1类资源分配下限值t1 即要求S1>=t1 否则不给予分配 一旦分配 进程对该类资源的需求值为d1 进行S1 = S1 - d1操作
特殊情况:Swait(S,1,0)(类似一个开关(if语句))当S>=1时 允许多个进程通过该区域 当S<1时阻止所有进程进入 因为他对该类资源的需求为0
信号量实现互斥
设置该类资源信号量初始值为1 然后将临界区
semaphore mutex=1;
p1(){
....
P(mutex);//在并发执行时无论P1的P(mutex)还是P2的P(mutex)先执行 另一边都无法执行
临界区代码
V(mutex);
....
}
P2(){
....
P(mutex)
临界区代码
V(mutex)
}
信号量实现前驱关系
想先执行进程P1中的S1原语再执行P2中的S2原语
semaphore mutex = 0;
//process P1 //process P2
while(1) while(1)
{ {
S1; wait(mutex);//只要P2的signal(mutex)不执行 Ps就无法执行
signal(mutex); S2;
} }
管程(类似于C++的类)
管程的作用:管程是供进程调用 每次也只能一个进程进入管程 有效的实现进程的互斥
管程的定义:一个管程定义了一个数据结构和能为并发进程所执行的一组操作
管程的组成:管程的名称(类名),局部于管程的共享数据结构说明(类的成员变量),对该数据结构进行操作的一组过程(类的成员函数方法),对局部于管程的共享数据设置初始值的语句(类的构造函数)
条件变量
管程的特性
模块化 管程是一个基本程序单位 可以单独编译
抽象数据类型 管冲中不仅有数据 而且有对数据的操作 (有成员变量也有成员函数)
信息隐蔽 管程中的数据结构只能被关程中的过程访问 (管程中的成员变量只能在成员函数中使用)
重点
条件变量的作用:防止某个进程在管程中挂起或阻塞了导致其他进程无法进入管程
条件变量定义方式: condition x;
条件变量的访问只能在管程中
条件变量自带两个操作和一个链表:每个条件变量保存一个链表 用于记录因该条件变量阻塞的所有进程
x.wait
正在调用冠城的进程因x条件需要被阻塞或挂起 则调用x.wait将自己插入到x条件的等待队列 释放管程
x,signal
正在调用管程的进程发现x条件发生了变化(一般就是放在释放资源只后调用) 则调用x.signal 重新启动一个因x条件而阻塞或者挂起的进程 如果没有则继续执行原进程
如果进程Q因x条件处于阻塞状态 然后调用管程的进程P执行了x.signal操作后 进程Q被重新启动
Hoare(应该是一个人)采用 Q立即执行 P等Q执行完再执行
Hansan (另一个人)他觉得signal操作是过程体的最后一个操作 所有进程P执行signal操作后立即退出管程 进程Q马上被恢复执行
感觉管程大概就像类
Monitor monitor_name//管程名
{
share variable zhengzaideclarations;//共享变量说明
condition declarations;//条件变量说明
public://能被进程调用的过程
void P1(...)//对数据结构操作的过程
{...}
void P2(...)
{...}//管程主体
{
initialization code; //初始化代码 就是直接写个大括号初始化就行
}
}
例子(生产消费者问题)
//N是公用缓冲池中具有N个缓冲区
Monitor Producerconsumer
{
/*变量说明*/ //成员变量
item buffer[N];
int in,out;//共享数据说明
condition notfull,notempty;//条件变量说明 //判断资源是否满或者空
int count; //缓冲池中已有的产品数目
public: //能被进程调用的过程
/*对数据结构操作的过程*/ //成员函数
void put(item x)
{
if(count >= N)cwait(notfull);//如果缓冲池已满 该生产者进程需要挂起等待 并将该进程放进notfull阻塞队列中
//cwait(notfull)应该等价于notfull.wait()
buffer[in] = x;
in = (in+1)%N;
count++;
csignal(notempty);//此时count增加了 公共数据发生了改变(也就是这里不再是空的了 那些因为仓库空而被挂起无法消费的消费者进程重新启动) 重新启动一个被挂起的进程 这里面填notempty是因为要重启的是放在notempty阻塞队列里面的进程
}
void get(item x)
{
if(count <= 0)cwait(notempty);
x = buffer[out];
out = (out+1)%N;
count--;
csignal(notfull);
}
{in = 0;out = 0;count = 0;} //这里就是初始化数据
}PC;
void produce()//这个是一个进程
{
item x;
while(TRUE)
{
...
procdece an item in nextp;
PC.put(x); //直接调用PC管程
}
}
void consumer()
{
item x;
while(TRUE)
{
PC.get(x);
consume the item in nextx;
...
}
}
void main()
{
cobegin
producer();consumer();
coend
}