同步互斥 信号量 管程
同步互斥的问题,并发进程如何实现多个进程都可以调用同一个功能,并且每次只能一个进程在使用(临界资源)?
比如打印机。
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变量,让进入管程而无法继续执行的进程阻塞自己.
管程(monitor)只是保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只被一个进程调用(由编译器实现).但是这样并不能保证进程以设计的顺序执行,因此需要设置condition变量,让进入管程而无法继续执行的进程阻塞自己.
在利用管程实现进程同步时,当某进程通过管程请求获得临界资源而未能满足时,管程便调用wait原语使该进程等待,并将其排在等待队列上。仅当另一个进程访问完成并释放该资源后,管程才又调用signal原语,唤醒等待队列中的队首进程。
但是,考虑这样一种情况:当一个进程调用了管程后,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除;在此期间,如果该进程不释放管程,则其它进程就无法进入管程,被迫长时间等待。
为了解决这个问题,引入条件变量condition。通常,一个进程被被阻塞或挂起的条件(原因)可有多个,因此在管程中设置了多个条件变量。
但是,考虑这样一种情况:当一个进程调用了管程后,在管程中时被阻塞或挂起,直到阻塞或挂起的原因解除;在此期间,如果该进程不释放管程,则其它进程就无法进入管程,被迫长时间等待。
为了解决这个问题,引入条件变量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(); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)