哲学家就餐问题(Dining Philosophers Problem)§

哲学家就餐问题是一个经典的并发控制问题。假设有五位哲学家围坐在一张圆桌旁,每位哲学家面前有一盘食物,以及左右两边各有一根筷子。哲学家有两种状态:思考和就餐。就餐时需要同时拿起左右两根筷子,思考时则放下筷子。问题在于如何设计一个算法,使得哲学家们能够顺利就餐,而不会出现死锁或饥饿现象。

package main

import (
    "sync"
)

// 哲学家数量
const philosopherNum = 5

// 筷子可以用互斥锁来模拟,每个筷子对应一个互斥锁
var chopsticks [philosopherNum]sync.Mutex

// Philosopher结构体代表哲学家
type Philosopher struct {
    id int
}

// Eat方法模拟哲学家就餐的操作
func (p *Philosopher) Eat() {
    left := p.id
    right := (p.id + 1) % philosopherNum

    // 先获取左边筷子
    chopsticks[left].Lock()
    // 再获取右边筷子,如果获取不到右边筷子,先释放左边筷子,避免死锁
    if ok := chopsticks[right].TryLock(); ok {
        println("Philosopher", p.id, "is eating.")
        chopsticks[right].Unlock()
    }
    chopsticks[left].Unlock()
}

func main() {
    var philosophers []*Philosopher

    // 创建5个哲学家
    for i := 0; i < philosopherNum; i++ {
        philosophers = append(philosophers, &Philosopher{id: i})
    }

    // 模拟哲学家们的就餐行为,多次循环体现持续的过程
    for j := 0; j < 10; j++ {
        for _, philosopher := range philosophers {
            go philosopher.Eat()
        }
        time.Sleep(100 * time.Millisecond)
    }
}