问题描述
某教学楼楼梯较窄,为了安全规定课间,一旦有人从上往下走,则不允许任何人从下往上走,但此时可以允许多人同时往下走,反之依然。请应用所学的同步操作完成此问题,要求不会发生饥饿现象。
版本一
思路:设置一个可通过楼梯的信号量,初始值为1(在这里信号量只有1,0两种状态,因此可看做锁)。楼梯上端为A,下端为B。下楼梯时在A处申请楼梯资源,如果楼梯空闲,则下楼梯,直到下楼梯状态完成到达B处,释放楼梯资源。上楼梯同理。
伪代码:
Semaphore ok_to_enter = 1;
PA
{
enter_A
{
P(ok_to_enter);
}
/* walk */
enter_B
{
V(ok_to_enter);
}
}
PB
{
enter_B
{
P(ok_to_enter);
}
/* walk */
enter_A
{
V(ok_to_enter);
}
}
存在的问题:只能单人通过楼梯,没有实现题目要求的可多人同时上或下楼梯的要求。
版本二
思路:在版本一基础上改进,可以参考读者写者问题的思路,设置两个变量,分别存储上下楼梯的人数,第一个下(上)楼梯的人申请楼梯资源,直到最后一个人完成下(上)楼梯操作才释放楼梯资源。这样就解决了多人同时上或下楼梯的需求。为了保证上下楼梯人数的正确,需加锁(mutexA、mutexB )来实现。
伪代码:
Semaphore mutexA = mutexB = 1;
Semaphore bridge = 1;
int countA = countB = 0;
PA
{
P(mutexA);
if(countA == 0)
P(bridge);
countA++;
V(mutexA);
/* walk */
P(mutexA);
countA--;
if(countA == 0)
V(bridge);
V(mutexA);
}
PB
{
P(mutexB);
if(countB == 0)
P(bridge);
countB++;
V(mutexB);
/* walk */
P(mutexB);
countB--;
if(countB == 0)
V(bridge);
V(mutexB);
}
存在的问题:可能会发生饿死现象。例如:下楼梯的人获取了楼梯资源,且一直持续不断有人下楼梯,那么等待上楼梯的人可能会“饿死”。
版本三
思路:为了不发生饿死的情况,在版本二上进行改进。变量含义在代码的注释中进行了说明,不再赘述。其思想是通过两个信号量来控制上下楼梯,满足条件(楼梯无人且上次通过的人与自己方向相反(或对面无人等待))的情况下获取上(下)楼梯的资源,完成上(下)楼梯的操作后,唤醒被阻塞的下(上)楼梯进程。(上下楼梯用的是同一个锁,所以会在pthread_mutex_lock(&m)
处阻塞。 )由于定义变量记录了前一个人上下楼梯的方向,在判断能否获得楼梯资源时对此进行判断,如果与之方向相反就存在“优先级”,从而可以保证上下楼梯两边的人尽量平等的获取资源,不会出现一方的人一直霸占楼梯资源的现象。
代码:说明:pthread_condwait(),pthread_cond_signal()
分别为P(),V()
操作。
pthread_mutex_t m;
pthread_cond_t turnA, turnB; //上下楼梯资源信号量
int waitingA, waitingB, AB_on_stair, BA_on_stair; //等待上、下楼梯人数,正在上、下楼梯人数
int pre = 0; //0表示A->B; 1表示B->A
PA
{
enter_A
{
pthread_mutex_lock(&m);
waitingA++;
while (BA_on_stair > 0 || prev == 0 && waitingB > 0))//判断条件
pthread_condwait(&turnA, &m);//阻塞时会释放锁
waitingA--;
AB_on_stair++;
prev = 0;
pthread_mutex_unlock(&m);
}
/* walk */
enter_B
{
pthread_mutex_lock(&m);
AB_on_stair--;
pthread_cond_signal(&turnB);
pthread_mutex_unlock(&m);
}
}
PB
{
enter_B
{
pthread_mutex_lock(&m);
waitingB++;
while (AB_on_stair > 0 || prev == 1 && waitingA > 0))
pthread_cond_wait(&turnB, &m);
waitingB--;
BA_on_stair++;
prev = 1;
pthread_mutex_unlock(&m);
}
/* walk */
enter_B
{
pthread_mutex_lock(&m);
BA_on_stair--;
pthread_cond_signal(&turnA);
pthread_mutex_unlock(&m);
}
}
可能说的不是特别清楚,如果实在不能理解的话,可以代入一个实例结合代码就会比较容易理解。