一、进程的互斥:
1.定义
比如一台计算机连着一台打印机,而且现在有多个打印进程,都想要进行打印,但是打印机不可能进行多个打印进程(如果这样做就乱套了),所以这些打印进程之间就存在着一种互斥关系,这时候 打印机可以被看成一种临界资源,在某个时刻内只能有一个进程占用这个临界资源,等这个进程结束,打印机资源才被释放,其他的进程才能使用这个临界资源
2.实现进程互斥的方法
因为打印机属于临界资源,所以可以用互斥信号量mutex表示,而且打印机只有一台,所以可以令mutex=1。
/*信号量机制实现互斥*/ semaphore mutex=1//定义并初始化信号量 P1(){ ... P(mutex); //使用临界资源前需要加锁 临界代码段.. V(mutex); //使用临界资源后需要解锁 ... } P2(){ ... P(mutex); //使用临界资源前需要加锁 临界代码段.. V(mutex); //使用临界资源后需要解锁 ... }
注:P,V操作就是 wait 和 signal 操作。wait 和 signal 是一对原语,可以把原语理解成我们自己写的函数,函数名为 wait
和 signal ,括号里面的信号量其实就是函数调用时传入的一个参数。
wait和signal函数原型:点击这里!!!
这里面有一点要注意,假设现在正在进行P1进程的P(mutex); 执行完这一行说不定就切换进程就去执行其他进程去了,原因可能是进程调度的方式不同,可能该进程的时间片用完了或者他的原因(原因有多种,这里只是说的一种可能情况)
二、进程的同步
1.定义
要让各并发按照要求有序的推进。
P1(){ 代码1; 代码2; 代码3; } P2(){ 代码4; 代码5; 代码6; }
比如上面的P1,P2进程并发执行,由于存在异步性,因此二者交替推进的次序是不确定的(可能会发生中途切换进程的情况)
但有时候我们会要求一定的顺序,比如(c=a+b;)执行这个代码之前要求我们首先计算出a和b的值,再去计算c的值。再比如上面的P1,P2进程,我们要求代码4要基于代码的程序运行,那么我们就要保证代码4一定实在代码2之后才会执行
这就是所谓的进程同步问题,让本来异步并发的进程互相配合,有序推进
2.实现进程同步的方法
用信号量实现进程同步:
1.分析为什么要实现”同步关系“,即必须保证”一前一后“执行的两个操作(或两句代码)
2.设置同步信号量S,初始值为0
3.在实现 ”前操作“之后执行 V(S) ------- signal 的加操作
4.在实现 ”后操作“之前执行 P(S) --------wait 的 减操作
还是上面的P1 ,P2进程,要求还是代码4要在代码2执行之后再执行,利用下面的方法即可实现
/*信号量机制实现同步*/ semaphore S=0; //初始化同步信号量 P1(){ 代码1; 代码2; V(S); 代码3; } P2(){ P(S); 代码4; 代码5; 代码6; }
分析:
情况一:若先执行到V(S) 操作,则S++后,S=1,之后执行到P(S)操作时,由于S=1,表示用可用的资源,会执行S--,
S的值变回到0,P2进程不会执行block原语,而是继续往下执行代码4。符合题意
情况二:若先执行到P(S)操作,由于S=0,S--后,表示此时没有可用资源,因此P操作中会执行block原语,主动请求阻塞,之后当执行完代码2,继而执行V(S)操作,S++,使S变回到0,由于此时又进程在该信号量的阻塞队列中,因此会在V操作中执行wakeup原语,唤醒P2进程,这样P2进程就可以继续执行代码4了。符合题意
上面两种情况保证了代码4执行前代码2肯定已经执行了