BarberShop
Posted on
理发师问题是操作系统中一个非常经典的同步问题,假设理发店里有一位理发师,一把理发椅和5把供等候理发的顾客坐的椅子。如果当前没有顾客,则理发师躺在理发椅上睡觉,如果有顾客前来,他必须唤醒理发师,如果理发师正在理发但又有顾客前来,如果有空位置可坐,顾客就坐下来等待,否则则离开。
下面我们使用 golang
中的 for select
机制结合 goroutine
和 channel
来解决这个问题。
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 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) }
|