首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >识别金刚死锁。5个哲学家问题

识别金刚死锁。5个哲学家问题
EN

Stack Overflow用户
提问于 2021-06-26 16:54:01
回答 2查看 308关注 0票数 0

我把fatal error: all goroutines are asleep - deadlock!放到了行wg.Wait()上--大约有30%的运行都是这样的,其余的都没有出错。我想我使用WaitGroup的方式是错误的,但不知道我做错了什么。也许有人能帮我辨认我的窃听器?谢谢!

代码语言:javascript
复制
package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

const (
    numOfPhilosophers = 5
    numOfMeals = 3
    maxEaters = 2
)

var doOnce sync.Once

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 {
        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示例使其工作

EN

回答 2

Stack Overflow用户

发布于 2021-06-26 19:13:48

go vet命令说

代码语言:javascript
复制
./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

另一个问题是,在试图在finishEatingChannel上发送确认时,戈鲁廷斯(哲学家)有时会被阻塞,因为负责从这个未缓冲通道读取数据的goroutine (主机)正忙于发送权限。下面是代码的确切部分:

代码语言:javascript
复制
            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%可重复的。

固定版本的代码:

代码语言:javascript
复制
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
}
票数 1
EN

Stack Overflow用户

发布于 2021-06-26 19:17:02

最后一个goroutine不会退出,当它向finishEatingChannel通道写入时,它将在最后一个迭代中被阻塞,因为它没有使用者。finishEatingChannel没有使用者的原因是permissionFromHost函数中的select大小写正在写入permissionChannel<-true,但是没有permissionChannel的使用者,因为它正在等待被读取,所以我们有一个死锁。

您可以使permissionFromHost通道缓冲,它将解决问题。

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/68144512

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档