package main import ( "errors" "flag" "fmt" "math/rand" "strings" "sync" "time" ) type Fork struct{} type Philo struct { Name string LHS chan Fork RHS chan Fork Bites int Delay time.Duration TimeOut time.Duration } func (p Philo) rndDelay() { n := rand.Intn(int(p.Delay)) time.Sleep(time.Duration(n)) } func (p Philo) printState(s string) { fmt.Printf("%10s %s\n", p.Name, s) } type stateFn func() stateFn func (p *Philo) arrive() stateFn { p.printState("arrives") return p.hungry } func (p *Philo) hungry() stateFn { p.printState("is hungry") <-p.LHS // grab left fork select { case <-p.RHS: // try to grab right fork return p.eat case <-time.After(p.TimeOut): p.LHS <- Fork{} // put left fork back return p.starve } return p.eat } func (p *Philo) starve() stateFn { p.printState("is starving") p.rndDelay() return p.hungry } func (p *Philo) eat() stateFn { p.printState("is eating") p.rndDelay() p.LHS <- Fork{} // release left fork p.RHS <- Fork{} // release right fork if p.Bites--; p.Bites <= 0 { return p.leave } return p.think } func (p *Philo) think() stateFn { p.printState("is thinking") p.rndDelay() return p.hungry } func (p *Philo) leave() stateFn { p.printState("leaves") return nil } func prepare(n int) []chan Fork { forks := make([]chan Fork, n) for i := range forks { forks[i] = make(chan Fork, 1) forks[i] <- Fork{} // put a fork } return forks } type Names []string func (n Names) String() string { return fmt.Sprint(strings.Join(n, ",")) } func (n *Names) Set(s string) error { *n = Names(strings.Split(s, ",")) if len(*n) < 2 { return errors.New("at least 2 names are required") } return nil } func main() { names := Names{"Aristotle", "Kant", "Spinoza", "Marx", "Russell"} bites := flag.Int("bites", 3, "number of rounds") delay := flag.Duration("max delay", 3*time.Second, "delay") timeOut := flag.Duration("timeout", time.Second, "delay") flag.Var(&names, "names", "philospher names") flag.Parse() rand.Seed(time.Now().UnixNano()) fmt.Println(len(names), "philosophers dining") defer fmt.Println("Table is empty") forks := prepare(len(names)) wg := sync.WaitGroup{} defer wg.Wait() for i, name := range names { wg.Add(1) p := &Philo{ Name: name, LHS: forks[i], RHS: forks[(i+1)%len(names)], Bites: *bites, Delay: *delay, TimeOut: *timeOut, } go func(p *Philo) { defer wg.Done() for state := p.arrive; state != nil; { state = state() } }(p) } }