分类互斥
有两类进程 A 和 B,一个资源 T。
- 两类不能同时使用一个资源 T。
- 可以有至多 N 个 A 同时使用 T,或者至多 M 个 B 同时使用 T。
semaphore cntA = 0;
semaphore cntB = 0;
semaphore mutexT = 1;
semaphore mutexA = 1;
semaphore mutexB = 1;
semaphore sa = N;
semaphore sb = M;
A()
{
P(mutexA);
if (cntA == 0)
P(mutexT);
cntA++;
V(mutexA);
P(sa);
// use T
V(sa);
P(mutexA);
cntA--;
if (cntA == 0)
V(mutexT);
V(mutexA);
}
B()
{
P(mutexB);
if (cntB == 0)
P(mutexT);
cntB++;
V(mutexB);
P(sb);
// use T
V(sb);
P(mutexB);
cntB--;
if (cntB == 0)
V(mutexT);
V(mutexB);
}
这个问题有生产者消费者问题的影子,也有读者写者问题的影子。
主要的考虑方向是,记录 A 和 B 使用 T 的数目。对于 A,如果发现当前没有 A 使用 T,那么有可能是有 B 正在使用 T,也有可能是 T 没有被使用,这个时候 A 实际上是在申请 T 这个资源。
同理当使用 T 结束,如果没有 A 使用 T,那么实际上是释放了这个资源。
同时应该认识到的是,cntA
不能被多个 A 同时访问,否则会导致多个等待,后者不能唤醒。使用 mutexA
对 cntA
的使用做管理。这样每个时刻最多会有一个 A 在等待 mutexT
,后来的 A 在等待 mutexA
。
协调生产
两个生产进程 A 和 B,产出产品数量不能有太大的差距:
−M≤|A−B|≤N
解决时,看作:
A≤B+NB≤A+M
semaphore sa = N; //
semaphore sb = M; //
A()
{
for (; ; ) {
P(sa);
// produce A
V(sb);
}
}
B()
{
for (; ; ) {
P(sb);
// produce B
V(sa);
}
}
sa
和 sb
分别表示 A 和 B 还可以生产多少个。
每个 A 产品产出都使得 B 可以多生产一个;每个 B 产品产出都使得 A 可以多生产一个。
这就是同步设计的基本模型。