同步互斥 信号量 管程

同步互斥的问题,并发进程如何实现多个进程都可以调用同一个功能,并且每次只能一个进程在使用(临界资源)?

比如打印机。

1,硬件禁止中断。

  禁止了中断,所以不存在判断和操作之间被中断的情况。

2,软件实现(同级)

  双线程:peterson 算法。大意:如果不是对方在用,我就可以用。

复制代码
boolean flag[2];
int turn;
void procedure0()
{
while(true)
{
flag[0]=true;//先标记
turn=1;//看对方有没有在用。
while(flag[1]&&turn==1) 

/*若flag[1]为false,P0就进入临界区;若flag[1]为tureP0循环等待,只要P1退出临界区,P0即可进入*/ { /* donothing*/ } visit();/*访问临界区*/ flag[0]=false;/*访问临界区完成,procedure0释放出临界区*/ /*remainder section*/ } } void procedure1() { while(true) { flag[1]=true; turn=0; while(flag[0]&&turn==0) { /* donothing*/ ; } visit();/*访问临界区*/ flag[1]=false;/*访问临界区完成,procedure1释放出临界区*/ /*remainder section*/ } } void main() { flag[0]=flag[1]=false; /*start procedure0 and procedure1*/ ; }
复制代码

    

 

  多线程: Dekkers 算法。

    

复制代码
1)P0的逻辑
do{
flag[0] = true;// 首先P0举手示意我要访问
while(flag[1]) {// 看看P1是否也举手了
if(turn==1){// 如果P1也举手了,那么就看看到底轮到谁
flag[0]=false;// 如果确实轮到P1,那么P0先把手放下(让P1先)
while(turn==1);// 只要还是P1的时间,P0就不举手,一直等
flag[0]=true;// 等到P1用完了(轮到P0了),P0再举手
}
flag[1] = false; // 只要可以跳出循环,说明P1用完了,应该跳出最外圈的while
}
visit();// 访问临界区
turn = 1;// P0访问完了,把轮次交给P1,让P1可以访问
flag[0]=false;// P0放下手
2)P1的逻辑
do{
flag[1] = true;// 先P1举手示意我要访问
while(flag[0]) {// 如果P0是否也举手了
if(turn==0){// 如果P0也举手了,那么久看看到底轮到谁
flag[1]=false;// 如果确实轮到P0,那么P1先把手放下(让P0先)
while(turn==0);// 只要还是P0的时间,P1就不举手,一直等
flag[0]=true;// 等到P0用完了(轮到P1了),P1再举手
}
}
visit();// 访问临界区
turn = 0;// P1访问完了,把轮次交给P0,让P0可以访问
flag[1]=false;// P1放下手
复制代码

    大意:如果轮到我了,我就用,用完传给别人。

3,抽象实现(操作系统提供一些原子操作(优先级比客户进程高,所以不会被用户进程中断))

    信号量Semaphore实现:

      什么是信号 ? 由一个整数变量,原子操作P() 和V() 组成。

      就像一个火车站,有3条铁轨可以停靠,乘客上车。每当一个火车进站的时候,调用P(),每当出站的时候,调用V();

      

复制代码
大意:
let sem = new Semaphore(x:number); class Semaphore{
  let val = x;
  P(){
    if(--this.val < 0){
      //wait in line
        WaitingQueue.push(Task)
    }else{
    // do the code  
    }
  };
  //after finished
  V(){
    if(-- this.val <= 0){
      //that means there's some process waiting in line;
      //唤醒一个等待中的进程
      //weakUp(Task)
    }
  }
}
复制代码

 

 

   管程: 建立在信号量之上的应用。(封装了同步操作)

  需要condition变量的原因本质上就是程序执行顺序的不确定性.

  管程(monitor)只是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行,因此需要设置condition变量,让进入管程而无法继续执行的进程阻塞自己.
 
  在利用管程实现进程同步时,当某进程通过管程请求获得临界资源而未能满足时,管程便调用wait原语使该进程等待,并将其排在等待队列上。仅当另一个进程访问完成并释放该资源后,管程才又调用signal原语,唤醒等待队列中的队首进程。
  但是,考虑这样一种情况:当一个进程调用了管程后,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除;在此期间,如果该进程不释放管程,则其它进程就无法进入管程,被迫长时间等待。
  为了解决这个问题,引入条件变量condition。通常,一个进程被被阻塞或挂起的条件(原因)可有多个,因此在管程中设置了多个条件变量。
 
复制代码
monitor {
  // define shared data
  condition x;//包括一个等待队列
  procedureA { 
    // do something
    x.signal();
  }
  procedureB {
    if(/* not meeting condition */)
      x.wait(); 
    // do something
  }
}
复制代码
有限缓冲,读写问题:
复制代码
class C{
    waiting_queue q;
    int wating_number=0;
  }

class Buffer{
  Lock lock;
  C notFull;//初始化C实例。
  C notEmpty;
  int count = 0;
}


复制代码
  
  C::wait( )-调用进程阻塞并移入与条件变量c相关的队列中,并释放管程,直到另一个进程在该条件变量C上执行signal( )唤醒等待进程并将其移出条件变量C队列。

  C::signal( )-如果存在其他进程由于对条件变量C执行wait( )而被阻塞,便释放之;如果没有进程在等待,那么,信号被丢弃。

复制代码
function Deposit(resource){
    lock->acquire();
    if(count==MAX){
        notFull.wait()//加入等notFull信号的等待队列q
    }
    //没有等待时;
    //add resource to buffer
    count++;
     //完成之后,通知读取
    notEmpty.signal();
    lock ->release();
}

function Remove(resource){
        lock->acquire();
    if(count==0){
        notEmpty.wait();//等待不空。
    }
    //count>0时
    //remove resource from buffer
    count--;
     //通知可写
   notFull.signal()//通知等待notFull信号的队列
    lock ->release();
}
复制代码

 

  

 

        

      

posted @   Esther_Cheung  阅读(208)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示