问题描述
五个哲学家共用一张圆桌,桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左右筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
约束条件
(1)只有拿到两只筷子时,哲学家才能吃饭;
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子;
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。
解决方法
使用state数组跟踪每个哲学家的状态(进餐、饥饿、思考)。
**饥饿状态:**哲学家在饥饿状态下试图拿筷子,需要读取周围邻居的状态,只有在两个邻居都没有进餐时才能同时拿到两边的筷子,进入进餐状态。就显然此时需要通过互斥锁保证读取的状态是不变的。若满足上述条件,则进入进餐状态。
**进餐状态:**进餐状态哲学家两边邻居则无法进餐。结束后需要放下筷子,将自身转为思考状态。同时为保证并行度,同时帮助两边邻居检查其状态,若邻居可以进餐,则帮他们解锁
package main
import (
"fmt"
"sync"
"time"
)
var (
thinking = 0
hungry = 1
eating = 2
philoCnt = 5
mutex = sync.Mutex{}
state []int
philoMutex []sync.Mutex
)
func thinkOrEat() {
time.Sleep(time.Second)
}
func leftPos(i int) int {
return (philoCnt + i - 1) % philoCnt
}
func rightPos(i int) int {
return (i + 1) % philoCnt
}
func test(i int) {
if state[i] == hungry && state[leftPos(i)] != eating && state[rightPos(i)] != eating {
state[i] = eating
philoMutex[i].Unlock()
}
}
func getForks(i int) {
mutex.Lock()
state[i] = hungry
test(i)
mutex.Unlock()
philoMutex[i].Lock()
}
func putForks(i int) {
mutex.Lock()
state[i] = thinking
test(leftPos(i)) // 检查左右的哲学家是否可以进餐,以保证整体获得最大的并行度
test(rightPos(i))
mutex.Unlock()
}
func philosopher(i int) {
for {
thinkOrEat() // thinking
getForks(i) // hungry
thinkOrEat() // eating
putForks(i)
}
}
func print() {
for {
mutex.Lock()
fmt.Println(state)
mutex.Unlock()
time.Sleep(100 * time.Millisecond)
}
}
func main() {
state = make([]int, philoCnt)
philoMutex = make([]sync.Mutex, philoCnt)
for i := range philoMutex {
philoMutex[i].Lock() // 先全部上锁
}
// i个哲学家开始就餐
for i := 0; i < philoCnt; i++ {
go philosopher(i)
}
// 监视进程
go print()
for {
thinkOrEat()
}
}