信号量和管程 \color{orange}{\huge{信号量和管程}} 信号量和管程
抽象数据类型 抽象数据类型 抽象数据类型
便于理解的解释:
\color{blue}{便于理解的解释:}
便于理解的解释:
1.最开始的时候两条铁轨上,没有没有火车,铁轨数量相当于信号量,当一辆火车进入铁轨的时候,执行
P
P
P操作,这个时候铁轨数量
−
1
-1
−1(
信号量数量
−
1
\red{信号量数量-1}
信号量数量−1)。
2. 当所有的铁轨都被占满的时候(
信号量数量
<
=
0
\red{信号量数量 <= 0}
信号量数量<=0),如果又来了火车,此时执行
P
P
P操作会因为
信号量数量
<
=
0
信号量数量 <= 0
信号量数量<=0而进入等待状态。
3. 当铁轨中有的火车运行完毕之后,从分叉铁轨出来的时候,执行
V
V
V操作,可使用铁轨数量
+
1
+1
+1(
信号量数量
+
1
\red{信号量数量 + 1}
信号量数量+1),并且向一个正在等待的火车传递信息(
睡眠线程的唤醒
\red{睡眠线程的唤醒}
睡眠线程的唤醒)。然后该等待的火车进入铁轨。
①. 信号量特征: 信号量特征: 信号量特征:
②. 信号量种类: 信号量种类: 信号量种类:
③. 信号量作用: 信号量作用: 信号量作用:
Class BoundedBuffer{ //缓冲区类型
mutex = new Semaphore(1); //互斥信号量
FullBuffers = new Semaphore(0); //缓冲区满信号量
EmptyBuffers = new Semaphore(n); //缓冲区空信号量
}
BoundedBuffer::Deposit(e) { //生产者的Deposit存储资源操作
EmptyBuffers -> P();
mutex -> P();
Add c to the buffer;
mutex -> V();
FullBuffers -> V();
}
BoundedBuffer::Remove(e) { //消费者的Remove取用资源操作
FullBuffers -> P();
mutex -> P();
Remove c from buffer;
mutex -> V();
EmptyBuffers -> V();
}
整体流程:
\blue{整体流程:}
整体流程:
D
e
p
o
s
i
t
:
\purple{\huge{Deposit:}}
Deposit:
生产者首先对于
E
m
p
t
y
B
u
f
f
e
r
s
EmptyBuffers
EmptyBuffers信号量执行
P
P
P操作希望能够获得向缓冲区存储数据的权利(
E
m
p
t
y
B
u
f
f
e
r
s
信号量的值代表缓冲区同时可以进入多少个生产者
\red{EmptyBuffers信号量的值代表缓冲区同时可以进入多少个生产者}
EmptyBuffers信号量的值代表缓冲区同时可以进入多少个生产者),之后对于
m
u
t
e
x
mutex
mutex信号量执行
P
P
P操作开启线程互斥(
m
u
t
e
x
=
1
→
m
u
t
e
x
=
0
mutex = 1 →mutex = 0
mutex=1→mutex=0),意为当前临界区已经被使用了,拒绝其他线程的访问。存储完数据之后,首先使用
m
u
t
e
x
mutex
mutex的
V
V
V操作打开临界区的访问权限(
m
u
t
e
x
=
1
→
m
u
t
e
x
=
0
mutex = 1 →mutex = 0
mutex=1→mutex=0),之后调用
F
u
l
l
B
u
f
f
e
r
s
FullBuffers
FullBuffers的
V
V
V操作告知消费者现在缓冲区里面的已经有了数据了(
F
u
l
l
B
u
f
f
e
r
s
信号量的值代表缓冲区中存储的数据个数
\red{FullBuffers信号量的值代表缓冲区中存储的数据个数}
FullBuffers信号量的值代表缓冲区中存储的数据个数)。
R
e
m
o
v
e
:
\purple{\huge{Remove:}}
Remove:
消费者首先对于
F
u
l
l
B
u
f
f
e
r
s
FullBuffers
FullBuffers执行
P
P
P操作,意为从缓冲区中读出数据(
F
u
l
l
B
u
f
f
e
r
s
信号量初始化的值是
0
,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据
\red{FullBuffers信号量初始化的值是0,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据}
FullBuffers信号量初始化的值是0,如果一开始没有数据,那么消费者会会被直接挡在这一步,等待消费者添加数据)。之后进行线程互斥操作,对
m
u
t
e
x
mutex
mutex信号量执行
P
P
P操作,然后取出数据。之后放开对于临界区的访问管控(
m
u
t
e
x
mutex
mutex = 0 →
m
u
t
e
x
mutex
mutex = 1)。最后对于
E
m
p
t
y
B
u
f
f
e
r
s
EmptyBuffers
EmptyBuffers信号量执行
V
V
V操作,这时可以将睡眠的生产者进程唤醒。
class Semaphore{
int sem;
WaitQueue q;
}
Semaphore::P(){ //P操作实现基本原理
sem--;
if(sem < 0){
Add this thread to q:
block(p);
}
}
Semaphore::V(){ //V操作实现基本原理
sem++;
if(sem <= 0){
Remove a thread t from q;
wakeup(t);
}
}
S e m + + 和 S e m − − 的理解: \color{red}{Sem++和Sem--的理解:} Sem++和Sem−−的理解:可以理解为,当 s e m < 0 sem < 0 sem<0的时候开始,对线程进行入队(等待队列)。 S e m − − Sem-- Sem−−就是对线程进行入队, S e m + + Sem++ Sem++就是对等待队列中的等待线程进行出队,然后调用 W a k e u p ( 出队线程 ) Wakeup(出队线程) Wakeup(出队线程)函数进行唤醒。
包含了一系列的共享变量以及操作这些共享变量的函数的集合。
管程构成: \red{管程构成:} 管程构成:
条件变量 W a i t 和 S i g n a l 操作实现: \red{条件变量Wait和Signal操作实现:} 条件变量Wait和Signal操作实现:
Class Condition {
int numWaiting = 0; //等待的线程数
WaitQueue q; //线程的等待队列
}
Condition::Wait(lock) { //将一个线程加入到等待队列中
numWaiting++;
Add this thread t to q;
release(lock);
schedule();
require(lock);
}
Condition::Signal() { //唤醒一个在等待队列中等待的线程
if(numWaiting > 0) {
Remove a thread t from q;
wakeup(t);
numWaiting--;
}
}
管程解决消费者——生产者问题 \color{purple}{管程解决消费者——生产者问题} 管程解决消费者——生产者问题
classBoundedBuffer { //字节缓冲区类构成
...
Lock lock; //共享锁变量
int count = 0; //线程中的数据数
Condition notFull , notEmpty; //条件变量
}
BoundedBuffer::Deposit(e) { //存储操作
lock -> Acquire(); //索要lock变量,锁住临界区
while(count == n) //如果数据已经满了,当前生产者的存放进程进入等待状态
notFull.Wait(&lock);
Add c to the buffer; //存放数据
count++;
notEmpty.Signal(); //Signal操作通知消费者可以进行取出数据了
lock -> Release(); //释放其他线程访问临界区的资格
}
BoundedBuffer::Remove(c) { //消费者取出资源操作
lock -> Acquire(); //索要访问临界区资源的权限
while(count == 0) //临界区没有资源了,当前消费者取出资源的操作进入等待状态
notEmpty.Wait(&lock);
Remove c from buffer;
count--;
notFull.Signal(); //向生产者发出信号,表示生产者可以继续存放数据了
lock -> Release(); //释放临界区访问权限
}
①.
哲学家用餐问题:
\red{哲学家用餐问题:}
哲学家用餐问题:
思路:
\color{olive}{思路:}
思路:
将每个哲学家的当前状态表示为临界区的临界资源。
每个哲学家访问临界资源的时候,应该是互斥的(
进程互斥
\color{blue}{进程互斥}
进程互斥)。
一个哲学家吃饱之后,可能会唤醒它的左邻右舍,两者之间存在着同步关系(
进程同步
\color{blue}{进程同步}
进程同步)。
伪代码实现:
\color{orange}{\huge{伪代码实现:}}
伪代码实现:
1.
t
a
k
e
_
f
o
r
k
s
take\_forks
take_forks函数:
2.
t
e
s
t
t
_
t
a
k
e
_
l
e
f
t
_
r
i
g
h
t
_
f
o
r
k
s
testt\_take\_left\_right\_forks
testt_take_left_right_forks函数
3.
p
u
t
_
f
o
r
k
s
put\_forks
put_forks函数