选择遗忘|面试官:这个经典的并发问题用 Go 语言如何实现?( 二 )
这边我为了示范 goroutine , 先用最笨的碰运气解法 , 也就是不刻意做任何资源配置 , 要在运气很坏的情况下才会遇上 livelock 。 什么是「运气很坏的情况」?
就是所有哲学家刚好在同一时间拿起同一边的叉子 。 但实际上 , 由于我给每位哲学家一个随机的思考时间 50mS(如下列代码) , 碰撞的机会是(1/50)^5 , 所以绝大部分情况下不会发生 livelock 。
func Think() { Random := func(max int) int {rand.Seed(time.Now().Unix())return rand.Int() % (max + 1) } <-time.After(time.Millisecond * time.Duration(Random(50)))}Wiki 上有介绍不需要碰运气 , 保证不会让 thread 饥饿致死的演算法 , 但我自己也没搞懂 , 请容我日后再介绍 。
解法与思路:1. 所用 Channel 型态与定位?本题采用 5 个 buffered channel , 分别代表 5 支筷子
type DiningPhilosophers struct { wg*sync.WaitGroup streamForks[5]chan interface{} missingDoubleForkTimes int}// Channel 初始化for i := range diningPhilosophers.streamForks { diningPhilosophers.streamForks[i] = make(chan interface{}, 1)}初始化// 叫所有哲学家开始动作start := time.Now()for i := range diningPhilosophers.streamForks { diningPhilosophers.wg.Add(1) go diningPhilosophers.WantToEat(i, PickLeftFork, PickRightFork, Eat, PutLeftFork, PutRightFork)}这边开始计时后 , 是一个 foreach 。
老方法 , 用 sync.WaitGroup 同步 5 个哲学家 goroutine 结束时间 。 给每一位哲学家起一个「WantToEat」的 goroutine , 告诉他 i 你是几号?又给入「PickLeftFork, PickRightFork, Eat, PutLeftFork, PutRightFork」五个函式的的 function reference 。
2. 五个 goroutine 之间 , 如何交接棒?没有交接棒问题 , 每位哲学家就凭运气去抢左右边的两只筷子 。 要注意的只有三件事情:
- 无法同时抢到两只筷子的哲学家 , 必须先放弃到手的一支筷子 。
- 已经同时抢到两只筷子的哲学家 , 吃完就必须退出餐桌 。
- 还没吃到的哲学家 , 可以无限次抢 。
实作前述的三条规范的 WantToEat()
- 本质上就是代表「还没吃到的哲学家 , 可以无限次抢」的无限回圈 。
- 「已经同时抢到两只筷子的哲学家 , 吃完就必须退出餐桌」是此回圈的结束条件 。
- 「无法同时抢到两只筷子的哲学家 , 必须先放弃到手的一支筷子 。 」是此回圈其中一个分支 。
func (this *DiningPhilosophers) WantToEat(philosopher int, pickLeftFork func(int), pickRightFork func(int), eat func(int), putLeftFork func(int), putRightFork func(int)) { defer this.wg.Done() var leftNum = (philosopher + 4) % 5//取得该哲学家左边的号码 var rightNum = (philosopher + 6) % 5 //取得该哲学家右边的号码 for {select {case this.streamForks[leftNum] <- philosopher: //尝试拿起左边筷子PickLeftFork(philosopher) //成功拿起左边筷子select {case this.streamForks[rightNum] <- philosopher: //尝试拿起右边筷子PickRightFork(philosopher)//成功拿起又边筷子Eat(philosopher)//左右边都拿到了 , 开始吃<-this.streamForks[leftNum] //吃完了 , 放下左边筷子PutLeftFork(philosopher)<-this.streamForks[rightNum] //吃完了 , 放下右边筷子PutRightFork(philosopher)return //吃饱离开default: //无法拿起右边筷子fmt.Printf("Philosopher %d can't pick fork %d.\n", philosopher, rightNum)
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 窘境|窘境中求助惨遭拒绝!中国此次也选择置之不理,俄国:早该如此
- 五商文化资讯微软选择“沉海”,华为却深藏贵州大山!阿里亚马逊也纷纷布局
- 动力|迈腾,君越,雅阁,凯美瑞之间选择,油耗低动力好,哪款更适合?
- 英超|兰帕德后悔么?放弃他成最差选择,26岁弃将如今是英超黑马缔造者
- 家电消费网| 副总裁:500万用户选择了OPPO的IoT,OPPO发布智能电视
- 网友|面试时话都没讲就赶人走?”,杭州小伙想不通:“就因为家里拆迁了
- 晴晴侃游戏|盗贼和狂暴战谁更适合呢,魔兽怀旧服咸鱼剑近战该如何选择
- 内蒙古|菅义伟第一次出访,为啥选择越南和印尼而不选择美国?
- 网络游戏|魔兽怀旧服咸鱼剑近战该如何选择,盗贼和狂暴战谁更适合呢
- 极客码头|你是选择盒装CPU还是散装CPU?,如果能够节省你装机的预算
