一:关与记录型信号量,
里面主要是由一个wait操作和signal操作,利用他来实现的一般就是进程的互斥了。
semaphore mutex=1;
P1(){
while(1){
wait(mutex);##--
临界区;
signal(mutex);##++
剩余区;
}
}
P2(){
while(1){
wait(mutex);##--
临界区;
signal(mutex);##++
剩余区;
}
}
## 这里有两个进程1,2都争用一个临界区,这个时候又要实现进程的并行,所以可以用到记录型信号量来实现,当然还有其他的方法这里只对记录型信号量来讨论互斥。
P1 和P2的实现过程是这样的:(通俗篇)
S1:P1,P2没人知道谁先执行谁后执行,这里面因素有很多,微弱的电流,暗物质,巴拉巴拉一大堆客观因素(暂且称之为小弟)。。。
但是有一点是聪明的人类定义的逻辑,那就是mutex信号量(互斥信号量),它就如同这众多因素里面的老大,
可以扭转整个局面。此时呢,它初始值是1;我们定义时1就是允许执行的。
S2:所以此时,总要有一个先执行(在同时mutex=1的前提下,谁的优先级不由老大决定了,下面小弟们都蠢蠢欲动,决定各自为政,
都想决定大事儿,此时我们就睁只眼闭只眼"随机"去吧,),假设现在我们得知是P1先被决定为先执行了,此时mutex=0了。P1被安排了,
可以自己独享临界资源了。
S3:由于计算机中执行速度是"超速度的",所以此时P2()怎么的也执行了wait(mutex)了,所以此时的mutex值就从0变为-1了,此时状态是,
临界资源被P1占用着,考虑到分配的原子性,这边我们逻辑上通过mutex的值对应分配关系,此时逻辑上是P2已经就绪状态,就等临界
资源空闲立马执行了,所以此时mutex=-1反应的一个逻辑状态是:P1在使用临界资源,在临界区执行着(ing时态),
P2处于一切就绪等待("被分配到阻塞队列中了")进入临界区状态,此时需要mutex决定P2的进入与否,也就是需要P1的唤醒了。
S4:终于要P1享用完了,美滋滋,开开心心的按下结账按钮,也就是执行到了signal(mutex)了,此时呢由于P1是彻底释放资源了,所以执行
signal(mutex)操作后mutex=0("取值范围mutex=-1,0,1"),因为P2已经进入了就绪等待状态("已经在阻塞队列中了"),一但mutex的值增
加了,就会立马被唤醒直接进入临界区操作,不会再次执行P操作(wait(mutex)),所以此时mutex=0,就直接变成了P2在临界区了,
P1已经出去了。接着P2临界区执行完毕,执行V操作(signal(mutex)),此mutex=1了,此时二者均没被使用,结束一个完整的流程。
特殊情况: 当然如果你还要追根问底,探索其他情况,比如说S4步骤开始P1执行完了,可是谁知道这个进程背后程序员设计的程序会不会紧接着
一行写到又要调用同一个临界资源呢,所以这时P2很不幸,又要被P1欺负,争抢,首先我们已经知道此时的"mutex=0",由于此时P1是刚执完的,
不像P2是经历过mutex=-1的人,已经早早的排起了队("阻塞队列"),P1此时刚出来门口,此时又反悔想回去再退掉刚刚买的手机,
但是他此时也要按规矩去排队,而且一定是在P2后面(P2得意了一回,哈哈哈)。此时由于P2是被P1出来这个动作唤醒的,
所以直接进去就行了(不会再回头执行P操作,mutex不会被P2改变此时,此时的mutex=0,是被P1的V操作改变的),
此时P1的V操作执行了,将"mutex有变成了-1",那么就标志着P1排上队了("阻塞队列"),屋("临界区")里面有人。
关于mutex的总结
mutex=-1,0,1
当mutex=1时,两个互斥的进程都没有进入临界区;
当mutex=0时;两个互斥的进程有一个进入了临界区,但是没发生争执;
当mutex=-1时,两个互斥的进程有一个在临界区,有一个不在临界区,但是至少那个不在临界区的进程争执过,将mutex变成了-1,
那么同时他没争赢就会进入阻塞队列中,当代被唤醒立即进入
<<<不知道了流程的可以从参看我上面的例子,同时也方便理解,不求一知半解,但求追根问底!!!
二: 生产者,消费者问题
生产者消费者问题是这样的:
#include<pushAPoP.h>
int in = 0, out = 0;
int full = 0, empty = n, mutex = 1;
item buffer[n];
producer {
while( true ) {
wait( empty ); // 等待缓冲区有空闲位置,与互斥锁位置时绝对的,必须放在前面,包含锁。
wait( mutex ); // 保证在product时不会有其他线程访问缓冲区
// product
buffer.push( full, in ); // 将新资源放到buffer[in]位置
in = ( in + 1 ) % n;
signal( mutex ); // 唤醒的顺序可以不同
signal( full ); // 通知consumer缓冲区有资源可以取走
}
}
consumer {
while( true ) {
wait( full ); // 等待缓冲区有资源可以使用
wait( mutex ); // 保证在consume时不会有其他线程访问缓冲区
// consume
buffer.pop( out ); // 将buffer[out]位置的的资源取走
out = ( out + 1 ) % n;
signal( mutex ); // 唤醒的顺序可以不同
signal( empty ); // 通知缓冲区有空闲位置
}
}