BarberShop

理发师问题是操作系统中一个非常经典的同步问题,假设理发店里有一位理发师,一把理发椅和5把供等候理发的顾客坐的椅子。如果当前没有顾客,则理发师躺在理发椅上睡觉,如果有顾客前来,他必须唤醒理发师,如果理发师正在理发但又有顾客前来,如果有空位置可坐,顾客就坐下来等待,否则则离开。

下面我们使用 golang 中的 for select 机制结合 goroutinechannel 来解决这个问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
const (
CUTTING_TIME = 20
BARBERS_COUNT = 1
HALL_SITS_AMOUNT = 5
)

type Barber struct {
name int
}

type Client struct {
name int
}

clientProducer 模拟顾客前来,每隔一段随机的时间,前来一位顾客,cutHear 模拟理发师理发过程,理发结束时,向 finished 通道发送一个 barber ,表示该理发师已经空闲。

1
2
3
4
5
6
7
8
9
10
11
12
func clientProducer(clients chan *Client) {
for {
time.Sleep(time.Duration(rand.Intn(28) + 7)
clients <- &Client{}
}
}

func cutHear(barber *Barber, client *Client, finished chan *Barber) {
fmt.Printf("%v cutting hear for %v\n", barber.name, client.name)
time.Sleep(CUTTING_TIME * time.Millisecond)
finished <- barber
}
  1. 若一顾客进入理发店,理发师正在为别人理发,且等待室有空椅子,该顾客就找张椅子按顺序坐下;
  2. 若一顾客进入理发店,理发师正在为别人理发,且所有椅子都被占用了,该顾客马上离开。
  3. 若一顾客进入理发店,理发师在睡觉,则叫醒理发师为该顾客理发;
  4. 如果理发师工作完,发现还有在等待的顾客,则呼唤顾客前来理发。
  5. 若没有要理发的顾客,则理发师去睡觉;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
func BarberShop(clients <-chan *Client) {
freeBarbers := []*Barber{}
waitingClient := []*Client{}
syncBarberChan := make(chan *Barber)

for i := 0; i < BARBERS_COUNT; i++ {
freeBarbers = append(freeBarbers, &Barber{})
}

for {
select {
case client := <-clients:
if len(freeBarbers) == 0 {
if len(waitingClient) < HALL_SITS_AMOUNT {
waitingClient = append(waitingClient, client)
fmt.Printf("Client is waiting in hall (%v)\n", len(waitingClient))
} else {
fmt.Println("No free space for client")
}
} else {
barber := freeBarbers[0]
freeBarbers := freeBarbers[1:]
fmt.Println("Client goes to barber")
go cutHear(barber, client, syncBarberChan)
}
case barber := <-syncBarberChan:
if len(waitingClient) > 0 {
client := waitingClient[0]
waitingClient = waitingClient[1:]
fmt.Printf("Take client from room (%v)\n", len(waitingClient))
go cutHear(barber, client, syncBarberChan)
} else {
fmt.Println("Barber idle")
freeBarbers = append(freeBarbers, barbers)
}
}
}
}

我们在 main 函数中起两个 goroutine ,分别代表顾客进程和理发师进程,理发师在工作8小时后退出,clients 通道作为两个 goroutine 沟通的媒介,类似于生产者和消费者模式。

1
2
3
4
5
6
func main() {
clients := make(chan *Client)
go clientProducer(clients)
go BarberShop(clients)
time.Sleep(8 * time.Hour)
}
Pieces of Valuable Programming Knowledges