Рассмотрим следующую попытку реализации Обеденных философов с подпрограммами и каналами Go.
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
Вопрос : почему это происходит?
Что я уже знаю:
Программа завершится, если процедура main
завершена (даже если какая-то другаяподпрограммы все еще работают).
Подпрограмма go блокируется, если она пытается прочитать данные из канала, и этот канал пуст (т. е. никто не писал в него ранее).
Теперь main
пытается прочитать 5 раз из канала plates
, поэтому он не должен завершаться, пока подпрограмма philos
не будет выполнена пять раз.Но, похоже, ему все равно удается прекратить, прежде чем сделать это.Я что-то пропустил?(Кажется, что plates
было прочитано только 4 раза.)
EDIT : Хорошо, подумав немного об этом, я пришел к выводу, что, возможно, 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
}
Тем не менее, было бы здорово, если бы кто-то смог подтвердить это объяснение:)