题目信息
思路详解
caltrain
这道题的主要思路是,车站里乘客分为两种,一种是候车乘客,一种是检票等车旅客。每当到来一个乘客,就增加一个乘客线程,乘客必须等待火车到来并且还有座位才能去排队检票等车;火车必须等待所有乘客都检票完成上车才能离开。
ok,那么按照思路,我们可以看到两个等待的过程,一个是乘客等车,一个是火车等乘客,所以可以设置两个信号量,作为唤醒等待的标志。
struct station {
struct lock mutex; //control critical section
struct condition arrival; //signal when train arrives station
struct condition leaving; //signal when train is ready to leave
int seats; //available seats
int waiting_passengers; //passengers on station
int leaving_passengers; //passengers on board
};
之后初始化,由于最开始没有车到站也没有乘客来,所以这些变量都是0。
void
station_init(struct station *station)
{
station->seats=0;
station->waiting_passengers = 0;//in station
station->leaving_passengers = 0;//on board
lock_init(&(station->mutex));
cond_init(&(station->arrival));
cond_init(&(station->leaving));
}
按照题目要求给出的三个函数,现在逐一解决。
第一个函数表示列车到站,或者说用来控制列车的行为,当车辆到站时会唤醒(cond_broadcast)所有的乘客线程,之后等待(cond_wait)可以离开的信号。
需要注意的是当车站没人或者这个车开过来的时候就一个座位都没有,就不必等待可以直接返回了。这个时候,因为会有乘客同时一下子都来的情况,所以必须要加锁。
void
station_load_train(struct station *station, int count)
{
lock_acquire(&(station->mutex));
if(count == 0 || station->waiting_passengers == 0){ //passenger may come at the same moment train arrives
lock_release(&(station->mutex)); //Race_Condition, must use mutex here
return;
}
station->seats = count;
cond_broadcast(&(station->arrival),&(station->mutex));//wake up all waitiing passengers
cond_wait(&(station->leaving), &(station->mutex));//waiting to leave
lock_release(&(station->mutex));
}
第二个函数表示乘客候车,车站来一个乘客候车人数就加一,当没有座位时,乘客就要等待新的列车到来。如果还有座位,乘客就去排队检票,检票人数增加,候车人数减少。
void
station_wait_for_train(struct station *station)
{
lock_acquire(&(station->mutex));
station->waiting_passengers++;
while(station->seats == 0){
cond_wait(&(station->arrival),&(station->mutex));
}
station->seats--;
station->waiting_passengers--;
station->leaving_passengers++;
lock_release(&(station->mutex));
}
第三个函数表示乘客登车,只有检票的人全部上车,并且车站没有候车的人或者列车没有剩余座位了,才能告诉列车它可以离开了。
void
station_on_board(struct station *station)
{
lock_acquire(&(station->mutex));
station->leaving_passengers--;
if(station->leaving_passengers == 0 && (station->seats == 0 || station->waiting_passengers == 0))
cond_signal(&(station->leaving),&(station->mutex));
lock_release(&(station->mutex));
}
测试也没什么问题啦!
reaction
踩坑记录:混淆signal用法,signal一次只能唤醒随机一个线程,我的1.0版本只用到了一个信号量,无法区别H和O唤醒哪个线程,就没有办法在产生一次反应时释放两个H一个O,以下是我的1.0错误示范:
#include "pintos_thread.h"
// Forward declaration. This function is implemented in reaction-runner.c,
// but you needn't care what it does. Just be sure it's called when
// appropriate within reaction_o()/reaction_h().
void make_water();
struct reaction {
struct lock mutex; //control critical section
struct condition StartReaction;
int waiting_H;
int waiting_O;
};
void
reaction_init(struct reaction *reaction)
{
reaction->waiting_H = 0;
reaction->waiting_O = 0;
lock_init(&(reaction->mutex));
cond_init(&(reaction->StartReaction));
}
void
reaction_h(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_H++;
while(reaction->waiting_H <2 || reaction->waiting_O<1){
cond_wait(&(reaction->StartReaction),&(reaction->mutex));
}
make_water();
cond_signal(&(reaction->StartReaction),&(reaction->mutex));
cond_signal(&(reaction->StartReaction),&(reaction->mutex));
reaction->waiting_H -= 2;
reaction->waiting_O --;
lock_release(&(reaction->mutex));
}
void
reaction_o(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_O++;
while(reaction->waiting_H <2 || reaction->waiting_O<1){
cond_wait(&(reaction->StartReaction),&(reaction->mutex));
}
make_water();
cond_signal(&(reaction->StartReaction),&(reaction->mutex));
reaction->waiting_H -= 2;
reaction->waiting_O -=1;
lock_release(&(reaction->mutex));
}
回到正题,按照题目要求,需要每次反应都返回两个H一个O,两种不同的原子就需要分别用两个信号量返回。
struct reaction {
struct lock mutex; //control critical section
struct condition H_reaction;
struct condition O_reaction;
int waiting_H; //waiting H number
int waiting_O; //waiting O number
};
每当来了一个H,它就会告诉O我们这边来了一个H,之后就等待O在告诉它反应完成。
void
reaction_h(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_H++;
cond_signal(&(reaction->O_reaction),&(reaction->mutex)); //a H come
cond_wait(&(reaction->H_reaction),&(reaction->mutex)); //waiting for reaction
lock_release(&(reaction->mutex));
}
我将反应条件判断放在了O这边,当满足条件时就反应,同时唤醒两个H信号量让他们返回,不满足条件就一直while等待足够的H。
void
reaction_o(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_O++;
while(reaction->waiting_H <2 || reaction->waiting_O<1){
cond_wait(&(reaction->O_reaction),&(reaction->mutex)); //waiting for H and repeat the while until reaction
}
make_water();
reaction->waiting_H -= 2;
reaction->waiting_O -=1;
cond_signal(&(reaction->H_reaction),&(reaction->mutex));
cond_signal(&(reaction->H_reaction),&(reaction->mutex));
lock_release(&(reaction->mutex));
}
测试成功,project 0完成!
源代码
caltrain.c
#include "pintos_thread.h"
struct station {
struct lock mutex; //control critical section
struct condition arrival; //signal when train arrives station
struct condition leaving; //signal when train is ready to leave
int seats; //available seats
int waiting_passengers; //passengers on station
int leaving_passengers; //passengers on board
};
void
station_init(struct station *station)
{
station->seats=0;
station->waiting_passengers = 0;//in station
station->leaving_passengers = 0;//on board
lock_init(&(station->mutex));
cond_init(&(station->arrival));
cond_init(&(station->leaving));
}
void
station_load_train(struct station *station, int count)
{
lock_acquire(&(station->mutex));
if(count == 0 || station->waiting_passengers == 0){ //passenger may come at the same moment train arrives
lock_release(&(station->mutex)); //Race_Condition, must use mutex here
return;
}
station->seats = count;
cond_broadcast(&(station->arrival),&(station->mutex));
cond_wait(&(station->leaving), &(station->mutex));
lock_release(&(station->mutex));
}
void
station_wait_for_train(struct station *station)
{
lock_acquire(&(station->mutex));
station->waiting_passengers++;
while(station->seats == 0){
cond_wait(&(station->arrival),&(station->mutex));
}
station->seats--;
station->waiting_passengers--;
station->leaving_passengers++;
lock_release(&(station->mutex));
}
void
station_on_board(struct station *station)
{
lock_acquire(&(station->mutex));
station->leaving_passengers--;
if(station->leaving_passengers == 0 && (station->seats == 0 || station->waiting_passengers == 0))
cond_signal(&(station->leaving),&(station->mutex));
lock_release(&(station->mutex));
}
reaction.c
#include "pintos_thread.h"
// Forward declaration. This function is implemented in reaction-runner.c,
// but you needn't care what it does. Just be sure it's called when
// appropriate within reaction_o()/reaction_h().
void make_water();
struct reaction {
struct lock mutex; //control critical section
struct condition H_reaction;
struct condition O_reaction;
int waiting_H; //waiting H number
int waiting_O; //waiting O number
};
void
reaction_init(struct reaction *reaction)
{
reaction->waiting_H = 0;
reaction->waiting_O = 0;
lock_init(&(reaction->mutex));
cond_init(&(reaction->H_reaction));
cond_init(&(reaction->O_reaction));
}
void
reaction_h(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_H++;
cond_signal(&(reaction->O_reaction),&(reaction->mutex)); //a H come
cond_wait(&(reaction->H_reaction),&(reaction->mutex)); //waiting for reaction
lock_release(&(reaction->mutex));
}
void
reaction_o(struct reaction *reaction)
{
lock_acquire(&(reaction->mutex));
reaction->waiting_O++;
while(reaction->waiting_H <2 || reaction->waiting_O<1){
cond_wait(&(reaction->O_reaction),&(reaction->mutex)); //waiting for H and repeat the while until reaction
}
make_water();
reaction->waiting_H -= 2;
reaction->waiting_O -=1;
cond_signal(&(reaction->H_reaction),&(reaction->mutex));
cond_signal(&(reaction->H_reaction),&(reaction->mutex));
lock_release(&(reaction->mutex));
}