【发布时间】:2019-09-14 01:40:39
【问题描述】:
考虑以下尝试使用 Go 例程和通道实现 Dining Philosophers。
package main
import "fmt"
func philos(id int, left, right, plate chan bool) {
fmt.Printf("Philosopher # %d wants to eat\n", id)
<-left
<-right
plate <- true
left <- true
right <- true
fmt.Printf("Philosopher # %d finished eating\n", id)
}
func main() {
const numPhilos = 5
var forks [numPhilos]chan bool
for i := 0; i < numPhilos; i++ {
forks[i] = make(chan bool, 1)
forks[i] <- true
}
plates := make(chan bool)
for i := 0; i < numPhilos; i++ {
go philos(i, forks[(i-1+numPhilos)%numPhilos], forks[(i+numPhilos)%numPhilos], plates)
}
for i := 0; i < numPhilos; i++ {
<-plates
}
}
有时这会按预期工作,即所有哲学家都吃,例如:
Philosopher # 4 wants to eat
Philosopher # 3 wants to eat
Philosopher # 2 wants to eat
Philosopher # 1 wants to eat
Philosopher # 4 finished eating
Philosopher # 3 finished eating
Philosopher # 2 finished eating
Philosopher # 1 finished eating
Philosopher # 0 wants to eat
Philosopher # 0 finished eating
但是,有时会错过一位(或多位)哲学家(例如,哲学家#0,在以下情况下没有吃饭):
Philosopher # 4 wants to eat
Philosopher # 1 wants to eat
Philosopher # 3 wants to eat
Philosopher # 2 wants to eat
Philosopher # 4 finished eating
Philosopher # 0 wants to eat
Philosopher # 2 finished eating
Philosopher # 1 finished eating
Philosopher # 3 finished eating
问题是:为什么会发生这种情况?
我已经知道的:
如果
maingo 例程完成,程序将退出(即使其他一些例程仍在运行)。如果一个 go 例程尝试从一个通道读取,并且该通道是空的(即之前没有人写过它),它将阻塞。
现在,main 尝试从通道 plates 读取 5 次,因此它不应该终止,直到 philos 例程运行五次。但它似乎在某种程度上仍然设法在这样做之前终止。我错过了什么吗? (似乎plates 只被读取了 4 次。)
编辑:好吧,再想一想,我得出的结论是,philos 例程可能总是运行 5 次,但是,它可以被中断 在有时间打印哲学家吃的那个之前。事实上,如果我按如下方式更改顺序,它似乎总是有效:
func philos(id int, left, right, plate chan bool) {
fmt.Printf("Philosopher # %d wants to eat\n", id)
<-left
<-right
left <- true
right <- true
fmt.Printf("Philosopher # %d finished eating\n", id)
plate <- true
}
不过,如果有人能验证这个解释,那就太好了:)
【问题讨论】:
-
@downvoter:请详细说明这个问题有什么问题?也许我可以尝试改进它。