Consider the following attempt to implement Dining Philosophers with Go routines and channels.
package main
import "fmt"
func philos(id int, left, right, plate chan bool) {
fmt.Printf("Philosopher # %d wants to eat
", id)
<-left
<-right
plate <- true
left <- true
right <- true
fmt.Printf("Philosopher # %d finished eating
", 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
}
}
Sometimes this works as expected, i.e. all philosophers eat, e.g.:
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
However, sometimes, one (or more) philosophers are missed (e.g. Philosopher #0, did not eat in the following case):
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
Question is: why does this happen?
What I already know:
The program will exit if the
main
go routine finished (even if some other routines are still running).A go routine will block if it tries to read from a channel, and that channel is empty (i.e. no-one wrote to it previously).
Now, main
tries to read 5 times from the channel plates
, therefore it should not terminate until the philos
routine has run five times. But it seems it somehow still manages to terminate before doing that. Am I missing something? (It seems that plates
was read only 4 times.)
EDIT: Ok, after thinking about it a bit more, I came to the conclusion, that maybe the philos
routine does always run 5 times, however, it can be interrupted before having the time to print that the philosopher ate. Indeed, if I change the order as follows, it seems to be working always:
func philos(id int, left, right, plate chan bool) {
fmt.Printf("Philosopher # %d wants to eat
", id)
<-left
<-right
left <- true
right <- true
fmt.Printf("Philosopher # %d finished eating
", id)
plate <- true
}
Still, it would be great if someone could validate this explanation :)