信号量-解决进程同步和进程互斥
一、信号量类型:二元信号量和计数信号量
- 二元信号量主要解决的是进程的互斥问题(也就是说当多个进程想要访问的资源只有一份的时候或者说多个资源想要对同一个资源进行读写,我们就需要使用二元信号量),
- 而计数信号量可以用于解决进程同步的问题(根据我个人的理解是当多个进程访问同一 资源池-有多个相同的资源可供访问,此时需要的就是计数信号量)。
- 对于二元信号量, 信号量mutex的取值 范围为0和1,而对于计数信号量是一个数据结构(在Java中 是一个类)其中这个数据 结构包含两个变量,第一个是int型的value变量,第二 个是struct类型的指针, 这个指针是一个链表,用于表示处于就绪队列的进程
type struct{
int value;
struct process *list;
}Semaphore
二、信号量只允许三个标准操作
- 初始化、wait()-P操作、signal()-V操作
三、进程的临界区必须符合如下框架
Semaphore S;//初始值为1
do{
wait(S);
Critical Section;
signal(S)
Remainder Section;
}
四、wait()操作
wait(S){
value--;
if(value<0){ //注意这里是先--,所以是大于零
将该进程插入相应的等待队列;
block(); //阻塞一个进程
}
}
五、signal()操作
signal(S){
value++;
if(value <=0){
将当前进程从等待队s.queue中移除
wakeup(P);//唤醒一个阻塞的进程
}
}
六、使用信号量解决生产者消费者问题
三个信号量初始值:empty = N;full = 0; mutex = 1;
empty = N用于判断生产者是否可以继续生产
full = 0用于判断消费者是否可以继续消费
Mutex = 1用于判断是否有生产者或者消费之进入了临界区,放置两个进程同时进入
1、对于生产者一旦缓冲区满了,那么此时生产者就不能执行了,也就是我们需要一个技术信号量empty = N,生产者在插入Item到缓冲区的时候需要判断缓冲区是否已满,如果已满,那么就进入等待,如果未满则判断mutex,如果mutex为1表示生产者并没有进入临界区,此时可以进入临界区。当执行完临界区以后。我们的缓冲区队列多了一个Item,那么此时我们就需要执行single(full),然后执行single(mutex)。
生产者代码{
while(true){
item = produce_item();
wait(empty)
wait(mutex)
inset_item(Item);
single(mutex)
single(full);
}
}
2、对于消费者而言,如果想要进入临界区,那么首先需要执行wait(full)用于判断缓冲区中是否有Item,如果有则执行下一步wait(mutex)是否生产者在写数据,然后才能进入临界区,执行完以后就需要执行single(mutex),然后执行single(empty)
消费者代码{
while(true){
wait(full)
wait(mutex)
inset_item(Item);
single(mutex)
single(empty);
consumer_item(item);
}
}
信号量带来的问题:死锁