package main import ( "fmt" "runtime" ) type philosopher struct { i int forks [2]chan bool eating chan int done chan struct{} } func (p philosopher) run() { for { select { case <-p.done: return case <-p.forks[0]: p.eat() } } } func (p philosopher) eat() { select { case <-p.done: return case <-p.forks[1]: p.eating <- p.i p.forks[0] <- true p.forks[1] <- true runtime.Gosched() } } func startPhilosophers(n int) (chan struct{}, chan int) { philosophers := make([]*philosopher, n) chans := make([]chan bool, n) for i := range chans { chans[i] = make(chan bool, 1) chans[i] <- true } eating := make(chan int, n) done := make(chan struct{}) for i := range philosophers { var min, max int if i == n - 1 { min = 0 max = i } else { min = i max = i + 1 } philosophers[i] = &philosopher{i: i, forks: [2]chan bool{chans[min], chans[max]}, eating: eating, done: done} go philosophers[i].run() } return done, eating } func wait(c chan int) { fmt.Println(<- c) runtime.Gosched() } func main() { // Restrict go to 1 real thread so we can be sure we're seeing goroutines // and not threads. runtime.GOMAXPROCS(1) // Create a bunch of goroutines done, eating := startPhilosophers(20) // stop1 // Now turn up the number of threads so this goroutine is likely to get // scheduled on a different thread. runtime.GOMAXPROCS(runtime.NumCPU()) // stop2 // Now let things run. Hopefully we'll bounce around wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) wait(eating) close(done) fmt.Println("done") // stop3 }