进程同步与互斥

并发性,异步性,独立性

以进程QQ和Chrome的伪代码为例:

QQ()									Chrome()
{										{
	while(1)								while(1)
	{										{
		c=a+b;			(1)						x=y+z;     (1)
		b=a+c;			(2)						y=x*z;     (2)
		a=c+b;			(3)						if(x==y)   (3)
		if(a==b)								{
		{											z+=y;
			c=a*b;								}
		}									}
	}									}
}

CPU时间片是轮转的,可能QQ的(1)执行完跳到Chrome(1)(2),再回到QQ(2)等等情况,每一行代码没执行完也可能跳到另一个进程。
并发性:在一个系统中,拥有多个计算,这些计算有同时执行的特性,而且他们之间有着潜在的交互

  • eg QQ和Chrome执行一会这个,执行一会那个

异步性:进程以不可预知的速度向前推进。

  • 即QQ和Chrome执行多少行代码再去执行另一个进程的不确定的。

独立性:独立性是指不同进程的工作相互不影响,进程实体是一个能够独立运行、独立获得资源和独立接受调度的基本单位。

  • 即QQ和Chrome之间是没有通信的(通信需用到PV操作),是互不影响的。

临界资源

临界资源:一次只能被一个进程所使用的资源

1.硬件资源

打印机,网卡(硬件资源都可以表现为软件资源)

2.软件资源

共享变量(像吃鸡中的抢,只能一个人捡)

Faker()
{
	while(1)
	{
		if(gun==1)
		{
			gun=Faker;
		}
		else
		{}
	}
}
Rookie()
{
	while(1)
	{
		if(gun==1)
		{
			gun=Rookie;
		}
		else
		{}
	}
}

因为进程的并发性,上述代码就会造成资源抢夺的bug
解决方法

Faker()
{
	while(1)
	{
		交通灯
----------------------------
		if(gun==1)
		{
			gun=Faker;
		}
		else
		{}
----------------------------
		关交通灯
	}
}
Rookie()
{
	while(1)
	{
		交通灯
----------------------------
		if(gun==1)
		{
			gun=Rookie;
		}
		else
		{}
----------------------------
		关交通灯
	}
}

添加“交通灯”,打开灯(类似火车厕所灯,起标志作用,用信号量来填写),虚线内即临界区,离开临界区后关闭信号灯。

同步与互斥

同步
一种合作关系,像石头人和亚索,显示了一种对运行次序的制约关系。
YaSuo()
{
	while(1)
	{
		交通灯
		R:狂风绝息斩
	}
}
StoneMan()
{
	while(1)
	{
		R:势不可挡
		交通灯
	}
}
互斥
一种竞争关系,像吃鸡中捡枪,显示了对共享资源的竞争关系,和临界资源中相同。

互斥方法的实现

硬件方法

中断屏蔽法

进程切换的时机:
1进程结束时 (已经不存在竞争了,即不需要互斥)
2发生中断(1)I|O请求时(2)时间片用完

原子硬件指令法

是一种硬件电路实现

软件方法

即使用“交通灯”

单标志法

利用Turn,轮流传递的思想
缺点:生死系与别人

双标志先检查法

每个人有自己的标志(解决了但标志法的缺点)
“过于礼貌”:先确定其他进程不需要再自己获取资源
缺点:有漏洞可导致同时进入临界区(因为可同时检查对方标志)

双标志后检查法

每个人有自己的标志(解决了但标志法的缺点)
“过于强势”:先自己获取资源
缺点:饥饿现象

Peterson’s算法

真正的实现了互斥
i - - Faker
j - - Rookie
flag=true;
turn=对方;
临界区
flag=false;

flag[i]=true;turn=j;
while(flag[i]&&turn=j)
	临界区
flag[i]=false;

信号量的介绍

进程互斥的硬件实现方法

硬件方法软件方法

信号量的P,V操作

信号量典型考题

生产者消费者问题,读者写者问题解题思路

  1. 画图理解题目
  2. 判断题目类型
  3. 分析进程数目,每个进程对应一个函数,填写代码模板
  4. 补充基本代码(伪代码)
    只做一次?(不加while),不断重复?(while(1))
  5. 补充P,V代码
    根据每个进程所关心的事物去补充
  6. 检查调整代码
    检查多个P操作连续出现的地方是否可能出现死锁(如果只有一个P操作,不满足请求和保持,不可能死锁;某信号量的P,V连续出现不可能死锁)
    若果可能产生死锁,通常可以尝试调整P的执行顺序(eg:小和尚抬水问题)

代码是一步一步写出来的,是反复调整写出来的

生产者消费者问题

生产者消费者题型特征:

①存在一个容器,装有的东西≤容量
②存在一个会让剩余容量越来越小的生产者和一个会让剩余容量越来越多的消费者
例:

妈妈每次放一个苹果到桌子上,你每次从桌子上取一个苹果,但是放苹果和取苹果不能同时进行。
而且桌子上最多放10个苹果。请用P,V操作实现同步互斥

①画图理解题目
在这里插入图片描述
② 判断题目类型
是生产者消费者类型
③分析进程数目,填写代码模板
【分析】:生产者(妈妈)→(关心的是)剩余空间
        消费者(你)→(关心的是)已占用空间
【填写代码模板】

You()                  Mom()
{					  {							
	while(1)               while(1)
	{                      {
	}                      }
}                     }

④补充基本代码(伪代码)

You()                  Mom()
{					  {							
	while(1)               while(1)
	{                      {  
	   取一个苹果             放一个苹果         
	}                      }
}                     }

⑤补充P,V代码
根据每个进程所关心的事物去补充

semephore full = 0;//初始桌子苹果数为空
semephore empty = 10;//初始桌子剩余容量为10

You()                  Mom()
{					  {							
	while(1)               while(1)
	{                      {  
	   P(full);	             P(empty);//让空余容量-1
	   取一个苹果             放一个苹果 
	   V(empty);             V(full);//让桌子上苹果数量+1
	}                      }
}                     }

⑥检查调整代码
需要互斥完成的动作需要用P、V操作一个信号量去完成

semephore full = 0;//初始桌子苹果数为空
semephore empty = 10;//初始桌子剩余容量为10
semephore mutex = 1;//用于实现互斥的信号量

You()                  Mom()
{					  {							
	while(1)               while(1)
	{                      {  
	   P(full);	             P(empty);//让空余容量-1
	   P(mutex);             P(mutex);
	   取一个苹果             放一个苹果
	   V(mutex);             V(mutex);//保证放苹果的动作是互斥的 
	   V(empty);             V(full);//让桌子上苹果数量+1
	}                      }
}                     }

读者写者问题

读者与写者题型特征:
  1. 不关心资源的数量,只关心资源是否被占用。
  2. 写者会对资源改动,读者不会改变资源状态。

例:

有读、写两组进程,共享一个文件,多个读者可同时访问文件,但多个写者不能同时访问文件,写者和读者也不能同时访问文件。

①画图理解题目
在这里插入图片描述
② 判断题目类型
是读者写者类型
③ 分析进程数目,填写代码模板
【分析】:
    写者 →(关心的是)文件是否被占有
读者团:第一个读者→(关心的是)文件是否被占,没有则创立一个读者团
    中间读者→(什么都不关心)只会增加读者团人数
    最后一个读者→(什么都不关心)释放文件
【填写代码模板】

You()                  Mom()
{					  {							
	while(1)               while(1)
	{                      {
	}                      }
}                     }

④补充基本代码(伪代码)
写者:

 Writer1()              Writer2()
{					   {							
	while(1)               while(1)
	{                      {  
	   写                     写 
	}                      }
}                      }

读者:

 Reader1()               Reader2()
{					    {							
	while(1)               while(1)
	{                      {  
	   读                     读
	}                      }
}                       }

⑤补充P,V代码
根据每个进程所关心的事物去补充
写者:

semephore S = 1;//文件数量
 Writer1()              Writer2()
{					   {							
	while(1)               while(1)
	{                      {  
	   P(s);	             P(s);//查看文件是否被占用
	   写                     写 
	   V(s);                 V(s);//释放文件
	}                      }
}                      }

读者:

semephore S = 1;//文件数量
int count=0;
 Reader1()               Reader2()
{					    {							
	while(1)               while(1)
	{                      {  
	  if(count==0)P(s);	     if(count==0)P(s);//如果是读者团第一个人,问一下文件是否被占有
	   count++;               count++;//增加一个读者
	   读                     读
	   count--;               count--;//读者离开
	  if(count==0)V(s);      if(count==0)V(s);//如果是最后一个读者,则释放文件
	}                      }
}                       }

⑥检查调整代码
需要互斥完成的动作需要用P、V操作一个信号量去完成
写者:

semephore S = 1;//文件数量
semephore mutex = 1;//用于实现互斥的信号量
int count=0;
 Writer1()              Writer2()
{					   {							
	while(1)               while(1)
	{                      {  
	   P(s);	             P(s);//查看文件是否被占用
	   写                     写 
	   V(s);                 V(s);//释放文件
	}                      }
}                      }

读者:

semephore S = 1;//文件数量
semephore mutex = 1;//用于实现互斥的信号量
int count=0;
 Reader1()               Reader2()
{					    {							
	while(1)               while(1)
	{                      {  
	  P(mutex);              P(mutex);//和写进程是互斥的
	  if(count==0)P(s);	     if(count==0)P(s);//如果是读者团第一个人,问一下文件是否被占有
	   count++;               count++;//增加一个读者
	   读                     读
	   count--;               count--;//读者离开
	  if(count==0)V(s);      if(count==0)V(s);//如果是最后一个读者,则释放文件
	  V(mutex);              V(mutex);
	}                      }
}                       }

某男子足球俱乐部,有教练、队员若干。每次足球训练开始之前,教练、球员都需要先进入更衣室换衣服,可惜俱
乐部只有一个更衣室。教练们脸皮薄,无法接受和别人共用更衣室。队员们脸皮厚,可以和其他队员一起使用更衣
室。如果队员和教练都要使用更衣室,则应该让教练优先。请使用P、V操作描述上述过程的互斥与同步,并说明所
用信号量及初值的含义

【问题分析】本题中,教练就是写者,队员就是读者,不过是读者写者问题换了个⻢甲而已。按照题目要求,要求实现“写优先”。(上题为读优先相当于一个门,写优先相当于两个门)

semophore read=1; //互斥信号量,用于给读者“上锁”
semophore write=1; //互斥信号量,用于给写者“上锁”
semophore rmutex=1; //互斥信号量,实现对readCount的互斥访问
semophore wmutex=1; //互斥信号量,实现对writeCount的互斥访问
int readCount=0, writeCount=0; //读者、写者的数量

//读者进程(在这个题里就是可以多人一起共用更衣室的队员们)
Reader(){
while(true){
	P(read); //每个读者到达时先对 read 上锁,为了实现写优先
	P(rmutex);
	readCount++;
	if(readCount==1) P(write); //第一个开始读的读者对写者上锁
	V(rmutex);
	V(read); //每个读者正式开始读之前对 read 解锁
	读者读...;
	P(rmutex);
	readCount--;
	if(readCount==0) V(write); //最后一个读完的读者对写者解锁
	V(rmutex);
}
}

//写者进程(在这个题目里,对应必须独享更衣室的教练们)
Writer(){
while(true){
	P(wmutex);
	writeCount++;
	if(writeCount==1) P(read); //第一个到达的写者对读者上锁,这一步是实现“写优先”的关键
	V(wmutex);
	P(write); //每个写者开始写之前都要 P(write),保证写者之间互斥,同时也能保证若当前有读者
	正在读,那么写者等待
	写者写...;
	V(write);
	P(wmutex);
	writeCount--;
	if(writeCount==0) V(read); //最后一个写者写完之后,对读者解锁
	V(wmutex);
}
}

对于读写公平

semaphore rw=1; //用于实现对共享文件的互斥访问
int count = 0; //记录当前有几个读进程在访问文件
semaphore mutex = 1; //用于保证对count变量的互斥访问
semaphore queue = 1; //用于实现“读写公平”。注:可以将queue理解为一个“队列”,当资源暂
不可访问时,无论读者、写者都需要公平排队
writer (){
while(1){
	P(queue);
	P(rw);
	写文件...
	V(rw);
	V(queue);
}
}

reader (){
while(1){
	P(queue);
	P(mutex);
	if(count==0)
	P(rw);
	count++;
	V(mutex);
	V(queue);
	读文件...
	P(mutex);
	count--;
	if(count==0)
	V(rw);
	V(mutex);
}
}

对于三种优先类型,如果弄不清楚它们之间的区别,不妨带入一个例子看看。假设每个读者的读操作都耗时较⻓,读者写者到达的顺序是:
读者1——读者2——读者3——写者A——读者4——写者B——读者5

如果采用 “读者优先” 的实现方法,那情况是这样的:读者1到达并开始读,紧接着读者2、读者3到达,都可以开始读;写者A到达,暂时不能写;读者4到达,可以开始读;写者B到达,暂时不能写;读者5到达,可以直接开始读;等读者1、2、3、4、5都读完之后,写者A、写者B才可以依次进行写。

如果采用 “读写公平法” 的实现方法,那情况是这样的:读者1到达并开始读,紧接着读者2、读者3到达,都可以开始读;写者A到达,暂时不能写;读者4到达,暂时不能读;写者B到达,暂时不能写;读者5到达,暂
时不能读;等读者1、2、3都读完之后,写者A开始写;写者A写完之后读者4开始读;读者4读完后写者B开始写;写者B写完后读者5开始读。

如果采用 “写优先” 的实现方法,那情况是这样的:读者1到达并开始读,紧接着读者2、读者3到达,都可以开始读;写者A到达,暂时不能写;读者4到达,暂时不能读;写者B到达,暂时不能写;读者5到达,暂时不能读;等读者1、2、3都读完之后,写者A开始写;写者A写完之后写者B开始写;写者B写完后读者4开始读,同时读者5也可以开始读

服务和被服务问题

需要用一个变量去描述等待的被服务者有多少个,这个变量需要保证互斥

理发师问题

问题描述:

理发店有一间工作室,有一名理发师,接待室内有n(n≥1)把椅子。如果没有顾客,理发师就去睡觉;如果理发师在睡觉;有顾客需要理发,则顾客会唤醒他;如果理发师在忙且接待室有空闲椅子,那么此顾客会坐在其中1把空闲的椅子上等待;如果来时所有椅子都有人,那么顾客离去。请采用信号量机制解决该理发师问题(用伪代码描述)

【分析】:
    理发师 →(关心的是)是否有顾客 是?服务:睡觉
     顾客→(关心的是)理发师是否在忙?
               不忙,在睡,叫醒
               忙,坐位是否够,够?坐下等:离开

1)设置理发师的资源信号量为barber,初值为初始状态可用的资源数,故设barber初值为0(因为没有顾客的时候理发师在睡觉呀)。
(2)设置顾客的资源信号量为customers,初值为0(刚开始没有顾客来)。
(3)用互斥信号量mutex实现进程互斥。
(4)用变量waiting来记录等待的顾客数,判断有没有空闲椅子。waiting变量需要保证互斥
semaphore barber,customers,mutex;
barber = customers = 0,mutex = 1;
int waiting = 0;
Tony{
	while(1){
		P(customers);//barber在等顾客喊他,没有顾客就睡觉,即阻塞等待
		P(mutex);
		waiting=waiting-1;//有顾客将得到服务
		V(mutex);
		V(barber);//理发师说“我来了”
		cut_hair();//理发师在工作
	}	
}
Customer{
	P(mutex);//保证waiting的互斥访问
	if(waiting<n){//来了个顾客在门口往店里看了看,如果有空闲的椅子
		waiting=waiting+1;//进店坐下了
		V(mutex);//访问waiting结束
		V(customers);//顾客跟理发师打招呼说“我来了”
		P(barber);//等待理发师
		get_haircut();//得到服务
	}else{//看了看发现店里没有位置
		V(mutex);//顾客离开,访问waiting结束
	}	
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值