为了账号安全,请及时绑定邮箱和手机立即绑定

识别戈兰死锁。5 哲学家问题

识别戈兰死锁。5 哲学家问题

Go
跃然一笑 2022-09-12 16:04:36
我正在排队 大约30%的运行都会发生这种情况,其余的都完成了,没有错误。我想我使用WaitGroup的方式是错误的,但不确定我做错了什么。也许有人可以帮助我识别我的错误?谢谢!fatal error: all goroutines are asleep - deadlock!wg.Wait()package mainimport (    "fmt"    "math/rand"    "sync"    "time")const (    numOfPhilosophers = 5    numOfMeals = 3    maxEaters = 2)var doOnce sync.Oncefunc main() {    chopsticks := make([]sync.Mutex, 5)    permissionChannel := make(chan bool)    finishEating := make(chan bool)    go permissionFromHost(permissionChannel,finishEating)    var wg sync.WaitGroup    wg.Add(numOfPhilosophers)    for i:=1 ; i<=numOfPhilosophers ; i++ {        go eat(i, chopsticks[i-1], chopsticks[i%numOfPhilosophers], &wg, permissionChannel, finishEating)    }    wg.Wait()}func eat(philosopherId int, left sync.Mutex, right sync.Mutex, wg *sync.WaitGroup, permissionChannel <-chan bool, finishEatingChannel chan<- bool) {    defer wg.Done()    for i:=1 ; i<=numOfMeals ; i++ {        //lock chopsticks in random order        if RandBool() {            left.Lock()            right.Lock()        } else {            right.Lock()            left.Lock()        }        fmt.Printf("waiting for permission from host %d\n",philosopherId)        <-permissionChannel        fmt.Printf("starting to eat %d (time %d)\n", philosopherId, i)        fmt.Printf("finish to eat %d (time %d)\n", philosopherId, i)        //release chopsticks        left.Unlock()        right.Unlock()        //let host know I am done eating        finishEatingChannel<-true    }}func permissionFromHost(permissionChannel chan<-bool, finishEating <-chan bool) {    ctr := 0    for {        select {        case <-finishEating:            ctr--        default:            if ctr<maxEaters {                ctr++                permissionChannel<-true            }        }    }}func RandBool() bool {    rand.Seed(time.Now().UnixNano())    return rand.Intn(2) == 1}编辑1:我修复了通过引用传递的互斥体。它没有解决问题。编辑2:我试图使用缓冲通道,使其工作permissionChannel:=make(chan bool, numOfPhilosophers)编辑3:也@Jaroslaw例子使它工作
查看完整描述

2 回答

?
蛊毒传说

TA贡献1895条经验 获得超3个赞

最后一个 goroutine 不会退出,当它写入通道时,它将在最后一次迭代中被阻止,因为它没有消费者。没有消费者的原因是函数中的选择情况正在写入,但没有消费者,因为它正在等待它被读取,所以我们有一个死锁。finishEatingChannelfinishEatingChannelpermissionFromHostpermissionChannel<-truepermissionChannel

您可以使权限从主机通道缓冲,它将解决此问题。

您的代码中还有一个错误,您正在按值传递互斥体,这是不允许的


查看完整回答
反对 回复 2022-09-12
?
慕后森

TA贡献1802条经验 获得超5个赞

该命令说go vet


./main.go:26:13: call of eat copies lock value: sync.Mutex

./main.go:26:30: call of eat copies lock value: sync.Mutex

./main.go:31:34: eat passes lock by value: sync.Mutex

./main.go:31:52: eat passes lock by value: sync.Mutex

另一个问题是,有时 goroutines(哲学家)在尝试发送确认时会被阻止,因为负责从此无缓冲通道读取数据的 goroutine(主机)正忙于尝试发送权限。以下是代码的确切部分:finishEatingChannel


            if ctr<maxEaters {

                ctr++

                // This goroutine stucks since the last philosopher is not reading from permissionChannel.

                // Philosopher is not reading from this channel at is busy trying to write finishEating channel which is not read by this goroutine.

                // Thus the deadlock happens.

                permissionChannel<-true 

            }

死锁是100%可重复的,当只剩下一个哲学家需要吃两次饭时。


固定版本的代码:


package main


import (

    "fmt"

    "math/rand"

    "sync"

    "time"

)


const (

    numOfPhilosophers = 5

    numOfMeals        = 3

    maxEaters         = 2

)


func main() {

    chopsticks := make([]sync.Mutex, 5)

    permissionChannel := make(chan bool)

    finishEating := make(chan bool)

    go permissionFromHost(permissionChannel, finishEating)

    var wg sync.WaitGroup

    wg.Add(numOfPhilosophers)

    for i := 1; i <= numOfPhilosophers; i++ {

        go eat(i, &chopsticks[i-1], &chopsticks[i%numOfPhilosophers], &wg, permissionChannel, finishEating)

    }

    wg.Wait()

}


func eat(philosopherId int, left *sync.Mutex, right *sync.Mutex, wg *sync.WaitGroup, permissionChannel <-chan bool, finishEatingChannel chan<- bool) {

    defer wg.Done()

    for i := 1; i <= numOfMeals; i++ {

        //lock chopsticks in random order

        if RandBool() {

            left.Lock()

            right.Lock()

        } else {

            right.Lock()

            left.Lock()

        }


        fmt.Printf("waiting for permission from host %d\n", philosopherId)

        <-permissionChannel


        fmt.Printf("starting to eat %d (time %d)\n", philosopherId, i)

        fmt.Printf("finish to eat %d (time %d)\n", philosopherId, i)

        //release chopsticks

        left.Unlock()

        right.Unlock()


        //let host know I am done eating

        finishEatingChannel <- true

    }

}


func permissionFromHost(permissionChannel chan<- bool, finishEating <-chan bool) {

    ctr := 0

    for {

        if ctr < maxEaters {

            select {

            case <-finishEating:

                ctr--

            case permissionChannel <- true:

                ctr++

            }

        } else {

            <-finishEating

            ctr--

        }

    }

}


func RandBool() bool {

    rand.Seed(time.Now().UnixNano())

    return rand.Intn(2) == 1

}


查看完整回答
反对 回复 2022-09-12
  • 2 回答
  • 0 关注
  • 152 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号