PPT上的PV操作:
classSemaphore { int sem; WaitQueue q; }
Semaphore::P() { sem--; if (sem < 0) { Add this thread t to q; block(p); } }
Semaphore::V() { sem++; if (sem<=0) { Remove a thread t from q; wakeup(t); } }
在课堂上,陈渝老师提到,能否将程序修改为如下情形(sem初始化是1):
P(){
if (sem <= 0) {
q. enqueue(t);
block(t);
}
sem--;
}
V() {
sem++;
if (!q.empty()) {
t = q.dequeue();
wakeup(t);
}
}
比如,一个例子:
(以下以Pn表示进程n执行P操作,Vn表示进程n执行V操作)
P1,sem=0,q=[],1进入临界区
P2,sem=0,q=[2],2被阻塞,不能执行sem--
V1,sem=1,q=[],2被唤醒,接着2执行sem--,sem=0,并且2进入临界区。
看似是没有问题的,但其实:
P1,sem=0,q=[],1进入临界区
P2,sem=0,q=[2],2被阻塞,不能执行sem--
V1,sem=1,q=[],2被唤醒,此时2处于就绪态,当进程调度时,如果没有马上调度2,比如调度了3,执行P3,3发现sem=1,于是直接跳到sem--并且进入临界区;而3在临界区的过程中,若发生进程切换,切到2时,2执行sem--,sem=-1,并且2也进入临界区。这就导致会有多个进程进入临界区的情况。因此这种做法是不可取的。