互斥:软件方法 Dekker互斥算法和Peterson互斥算法详解

2.13 互斥

要求
  • 空闲让进:若空闲,申请即进
  • 忙则等待:只允许临界区存在一个进程,若忙碌,区外等待
  • 有限等待:进程等待的时间是有限的,不会造成死锁、饥饿
  • 让权等待:进程不能在临界区长时间阻塞等待某事件
  • 以上类比生活中任何公共资源都可,如公用电话
2.13.1 互斥:软件方法
思路
  1. 在进入区设置标志来判断是否有进程在临界区
  2. 若临界区已有进程,则循环等待
  3. 进程离开临界区后在退出区修改标志
第一代:轮换使用临界区

每个进入临界区的进程的权限只能被另一个进程赋予

int turn=0;

//进程P0
do{
	while(turn!=0);//进入区
	P0代码;//退出区
	turn=1//退出区
}while(true)

//进程P1
do{
	while(turn!=1)//进入区
	P1代码;//临界区
	turn=0;//退出区
}while(true)

严格轮换,实现了互斥访问
违反了空闲让进的原则:比如turn=1时,临界区空,但P1无法进入

第二代:设置临界区状态标志

进入之前看有没有其他进程要进入,没有则将自己的状态置为true

boolean flag[2]={false,false}//该全局变量标志临界区空闲与否

//进程P0
do{
	while(flag[1]);//进入区
	flag[0]=true;//进入区
	P0代码;//临界区
	flag[0]=false;//退出区
}while(true)

//进程P1
do{
	while(flag[0]);//进入区
	flag[1]=true;//进入区
	P1代码;//临界区
	flag[1]=false;//退出区
}while(true)
	

违反了忙则等待原则:假设CPU先将时间片分给P0,然后P0判断flag[1]=false跳出循环向下执行,这时CPU又将时间片分给 P1,P0尚未执行到置flag[0]=true;P1判断flag[0]=false跳出循环向下执行,两者都将进入临界区
注意:while(flag[1]);这一句表明:当flag[1]=true时停在这一句,flag[1]=false时跳出循环执行下一句,P0即进入临界区

第三代:表明使用临界区的状态

先亮明状态,再循环等待

boolean flag[2] = {false, false};//共享的全局变量

//进程P0
do {
    flag[0] = true;    //进入区
    while (flag[1]) ;//进入区
    进程P0的临界区代码; //临界区
    flag[0] = false;   //退出区
    进程P0的其它代码     //剩余区
} while (true)

//进程P1
do {
    flag[1] = true;    //进入区
    while (flag[0]) ;//进入区
    进程P1的临界区代码; //临界区
    flag[1] = false;   //退出区
    进程P1的其它代码     //剩余区
} while (true)

违反了空闲让进原则:CPU先将时间片分给P0,然后P0置flag[0]=true,这时CPU又将时间片分给 P1,P1置flag[1]=true;那么接下来两者都在while循环处等待对方清零,都无法进入临界区,形成死锁

第四代:表明使用临界区的态度+谦让

预先表明想进入临界区,但为了防止死锁,在进入临界区前会延迟一段时间

boolean flag[2] = {false, false};//共享的全局变量

//进程P0
do {
    flag[0] = true;                 
    while (flag[1])  {              
        flag[0] = false;
         <随机延迟一小段时间>//谦让
        flag[0] = true;
    }
    进程P0的临界区代码; //临界区
    flag[0] = false;   //退出区
    进程P0的其它代码     //剩余区
} while (true)

//进程P1
do {
    flag[1] = true;                  
    while (flag[0]) {                
        flag[1] = false; 
        <随机延迟一小段时间>//谦让
      flag[1] = true;
    }
    进程P1的临界区代码; //临界区
    flag[1] = false;   //退出区
    进程P1的其它代码     //剩余区
} while (true)

可能会活锁,过分谦让,长时间僵持:CPU先将时间片分给P0,然后P0置flag[0]=true,这时CPU又将时间片分给 P1,P1置flag[1]=true;接下来CPU先将时间片分给P0,P0发现flag[1]=true;进入while循环,先将flag[0]清零,再挂起一段时间,重新置位flag[0],查看flag[1]是否仍为1,若是则继续清零等待,重复这个过程,监听flag[1]的变化;同理,P1可能会与P0默契地进行同样的活动,两者同时监听,同时挂起,形成活锁,可能随时间自动解除

Dekker互斥算法

由于前一种算法活锁的原因是只监听了对方的flag,这时添加一个只能由对方改变的信息turn即可
互斥:软件方法 Dekker互斥算法和Peterson互斥算法详解_第1张图片

boolean flag[2] = {false, false};      //共享的全局变量
int turn = 1;                          //共享的全局变量

//进程P0
do {
    flag[0] = true;              //进入区
    while (flag[1]) {
       if (turn == 1)  {
           flag[0] = false;
           while  (turn == 1) ;  //等待
           flag[0] = true;
        }
     }                           //进入区
    进程P0的临界区代码;           //临界区
    turn = 1;
    flag[0] = false;             //退出区
    进程P0的其它代码               //剩余区
} while (true)

//进程P1
do {
    flag[1] = true;              //进入区
    while (flag[0]) {
       if (turn == 0)  {
           flag[1] = false;
           while  (turn == 0) ;  //等待
           flag[1] = true;
        }
     }                           //进入区
    进程P1的临界区代码;           //临界区
    turn = 0;
    flag[1] = false;             //退出区
    进程P1的其它代码               //剩余区
} while (true)

在预先表明态度+谦让的基础上改进了谦让机制:
P1在退出临界区时会置turn=0;P0退出临界区会将turn置为1
这样一来,可以在监听对方flag时知道对方是否已经退出临界区,而不是等待一段随机时间,我认为这时重新尝试将自己的flag置为true是更好的做法,不会产生活锁

Peterson互斥算法

与dekker算法的区别:不是在退出时而是在进入前将turn改变

boolean flag[2] = {false, false};    //共享的全局变量
int turn;                            //共享的全局变量

//进程P0
do {
    flag[0] = true;                //进入区
    turn = 1;                      //进入区
    while (flag[1] && turn == 1) ; //进入区
    进程P0的临界区代码;             //临界区
    flag[0] = false;               //退出区
    进程P0的其它代码          	   //剩余区
} while (true)

进程P1
do {
    flag[1] = true;                       //进入区
    turn = 0;                             //进入区
    while (flag[0] && turn == 0) ; 	      //进入区
    进程P1的临界区代码;           		  //临界区
    flag[1] = false;                      //退出区
    进程P1的其它代码                   	  //剩余区
} while (true)

假设P0进程先置自己的flag为true,改变turn=1,此时有一个while循环,当P1的flag=false时,说明P1还没有运行到置flag=true的行,可以继续进入临界区,或当turn=0时,这时说明P1执行后转而执行P0,P0此时在turn行,但P0无法进入临界区,因为此时flag[0]=true,而此时P0可以无视flag[1]=true的条件而继续进入临界区

评价软件互斥方法
  1. 不能解决忙等问题(使用while监听方法),效率低
  2. 进程互斥使用临界资源困难,难以控制两个以上进程的互斥
  3. 算法设计时容易死锁或互斥失败

你可能感兴趣的:(操作系统,算法,多线程,操作系统)