2 回答
TA贡献1895条经验 获得超3个赞
最后一个 goroutine 不会退出,当它写入通道时,它将在最后一次迭代中被阻止,因为它没有消费者。没有消费者的原因是函数中的选择情况正在写入,但没有消费者,因为它正在等待它被读取,所以我们有一个死锁。finishEatingChannelfinishEatingChannelpermissionFromHostpermissionChannel<-truepermissionChannel
您可以使权限从主机通道缓冲,它将解决此问题。
您的代码中还有一个错误,您正在按值传递互斥体,这是不允许的
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
}
- 2 回答
- 0 关注
- 152 浏览
添加回答
举报
