文章目录
前言
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
一、进程同步的基本结构
以公共汽车的例子说明一下什么是进程同步:
如上图所示,司机进程和售票员进程分别要完成几项工作。但这几项工作并不是互相独立的,两个进程之间的工作是否执行,可能取决于另一个进程的工作是否执行。
例如司机在启动车辆之前必须确认售票员已经完成售票并且关闭了车门,而售票员打开车门的前提也是司机完成了车辆的停靠。因此,两个进程之间需要同步的地方就是在司机启动车辆前和售票员关闭车门前。
那么,在特定的时候需要让进程“睡眠”,去等待另一个进程的执行;而在另一个进程执行到相应的位置后,需要将“睡眠”的进程“唤醒”。
引入“睡眠”和“唤醒”后 ,司机和售票员的进程如上图所示。
这便是进程同步的基本结构。
二、信号
如上面的例子所示,我们解决进程不同步的方法就是引入“睡眠”和“唤醒”机制。
那么,什么时候睡眠、什么时候唤醒,这就需要信号来提供判断的依据。
1.“生产者”——“消费者”问题
以生产者——消费者问题为例:生产者进程和消费者进程共用一个数据缓存区,生产者向缓存区写入数据,而消费者则从缓存区取出数据;当缓存区满的时候,生产者就必须睡眠,等待生产者取出数据后将其唤醒,而缓存区为空时,消费者需要等待生产者向缓冲区写入数据。
我们定义empty和full两个信号:empty表示“空闲缓存区个数”,消费者进程用掉一个内容单元时,即产生一个空闲缓存区,就会向生产者进程发送一个empty信号;而生产者在向一个空闲单元填入内容后,也会向消费者发送一个full信号。
如上图所示,当counter == BUFFER_SIZE时,也就是说缓存区已经满了,生产者就会进入睡眠状态,等待信号empty;直到消费者使用了一个内存单元,发现counter == BUFFER_SIZE,有空闲的内存单元了,就会向生产者进程发送empty信号,将其唤醒。
但是如果存在两个消费者进程P1和P2的情况下,又会出现什么情况了?
当缓冲区满了,那么P1和P2进程都分别进入了阻塞状态,等待empty信号;而当消费者进程使用了一个内存单元后,判断counter == BUFFER_SIZE-1,就会向P1发送empty信号,P1就会被唤醒;而当消费者进程再循环一次,counter == BUFFER_SIZE-2,但此时并不会触发wake_up,P2无法被唤醒。
而为了解决这一问题,必须将信号转变为信号量。
2.将信号扩展为信号量
用counter来控制进程的走与停,用信号记录进程停和走的位置,无法应对多变的调度情况,这是应为counter表达出来的语义是不够的。当P1和P2都处在阻塞状态时,counter始终等于BUFFER_SIZE,并不能表现出目前阻塞的进程数量。
为了记录目前阻塞的进程数量,我们需要引入信号量(semaphore),同时还可以用它来控制进程的走或停。因此,信号量就是基于信号的一个整数,用来控制进程的阻塞或唤醒。
信号量empty关联着一个表达量的整数。假如P1阻塞在empty信号量上,empty=1,表示此时有一个进程阻塞;而当P2被阻塞后,empty就变成了2。如果消费者进程C循环了一次,唤醒了一个进程,那么empty=1,再唤醒一个,empty=0。
那么,如果C在循环一次,empty=-1,这种情况意味着什么了?
-1表示现在缓存区有一个空闲单元,对于生产者来说就是有了一个资源。但是用-1来表示有一个资源意义不太明了,我们将-1改为+1,那么+1即表示有一个空闲单元,而-1表示此时缺少一个资源,有一个进程出于阻塞状态。
引入信号量之后,所能表达出来的含义更多了:
(1)对empty而言,当它为整数时,就表明还有empty个资源,而为负数时,则表明还缺少|empty|个资源,进程处于阻塞状态。
(2)对生产者进程而言,当它发现empty为0或负数时,说明没有空闲单元,则进入阻塞状态。
(3)对消费者进程而言,如果empty为负数,表明有生产者进程处于阻塞状态,需要唤醒。
3.信号量的意义和实现
通过以上的例子,我们更加明确了信号量的意义:
(1)信号量就是一个整型变量,用来记录和进程同步有关的重要信息;
(2)能让进程阻塞睡眠在这个信号量上;
(3)需要同步的进程对信号量进行操作(加一减一)实现进程的阻塞和唤醒。
因此,信号量就是一个数据对象和对这个数据对象的两个操作。数据对象是信号量的数值和相应的阻塞进程队列;两个操作即对信号量的数值进行+1或-1操作。
struct semaphore
{
int value;//信号量的数值,用来记录资源个数或等待进程个数
PCB *queue;//等待在这个信号量上的队列
}
P(semaphore s)//进行-1操作
{
s.value--;
if(s.value < 0)
{
sleep_on(s.queue);
}
}
V(semaphore s)//进行+1操作
{
s.value++;
if(s.value <= 0)
{
wake_up(s.queue);
}
}
4.生产者——消费者问题的信号量解法
empty信号量表达的是缓存区中空闲单元的个数,初值为BUFFER_SIZE,生产者对它进行P操作,消费者对它进行V操作。
同样的道理,我们发现消费者进程再缓存区中没有有内容的单元时,会进入阻塞状态,我们用full这个信号量来表示有内容单元的数量,初值为0,生产者对它进行V操作,消费者对它进行P操作。
此外,由于是共享缓存区,同时只能有一个进程能够对缓存区内容进行修改,因此引入信号量mutex = 1,只有当数值为1的时候,才能进入缓存区进行修改。
总结
(1)说明了进程同步的概念,为什么要进行进程同步;
(2)使用信号实现进程同步,并发现信号表达的含义不够;
(3)引入信号量,对信号量进行定义,用信号量实现进程同步的过程。