Created
November 3, 2020 19:08
-
-
Save pbnjay/2aa3ad073391cedaab0be1b8228779cf to your computer and use it in GitHub Desktop.
Sleeping Barbers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I was inspired by @goinggodotnet to implement the sleeping barbers problem | |
// https://twitter.com/goinggodotnet/status/1323664013264375809 | |
// | |
// https://en.wikipedia.org/wiki/Sleeping_barber_problem | |
package main | |
import ( | |
"flag" | |
"log" | |
"math/rand" | |
"sync/atomic" | |
"time" | |
) | |
type Customer struct { | |
Name string | |
} | |
var Sleepytime = &Customer{"Napping..."} | |
func NewCustomer() *Customer { | |
firstName := firstNames[rand.Intn(len(firstNames))] | |
surName := surNames[rand.Intn(len(surNames))] | |
return &Customer{firstName + " " + surName} | |
} | |
func (c *Customer) CutFinished() { | |
log.Println(c.Name, "likes the haircut!") | |
atomic.AddUint32(&numCuts, 1) | |
} | |
func (c *Customer) Leave() { | |
log.Println(c.Name, "walked out the door") | |
atomic.AddUint32(&numLeft, 1) | |
} | |
type Barber struct { | |
Name string | |
cutting chan *Customer | |
} | |
func NewBarber() *Barber { | |
firstName := firstNames[rand.Intn(len(firstNames))] | |
surName := surNames[rand.Intn(len(surNames))] | |
return &Barber{ | |
Name: firstName + " " + surName, | |
cutting: make(chan *Customer, 1), | |
} | |
} | |
func (b *Barber) Work(w *WaitingRoom) { | |
for { | |
c, ok := w.FindCustomer() | |
if !ok { | |
log.Println(b.Name, "is taking a nap") | |
b.cutting <- Sleepytime | |
} else { | |
log.Println(b.Name, "is cutting", c.Name) | |
b.cutting <- c | |
} | |
dur := time.Second * time.Duration(rand.Intn(5)) | |
time.Sleep(dur) | |
// get out of the chair | |
<-b.cutting | |
if ok { | |
c.CutFinished() | |
c.Leave() | |
} | |
} | |
} | |
func (b *Barber) Checkin(c *Customer) bool { | |
select { | |
case b.cutting <- c: | |
return true | |
default: | |
return false | |
} | |
} | |
////// | |
type WaitingRoom struct { | |
seats chan *Customer | |
} | |
func NewWaitingRoom(nc int) *WaitingRoom { | |
return &WaitingRoom{seats: make(chan *Customer, nc)} | |
} | |
func (r *WaitingRoom) FindSeat(c *Customer) bool { | |
select { | |
case r.seats <- c: | |
// there's a chair free | |
log.Println(c.Name, "sat down in the waiting room") | |
return true | |
default: | |
// all the chairs are full | |
log.Println(c.Name, "left because the waiting room is full") | |
return false | |
} | |
} | |
func (r *WaitingRoom) FindCustomer() (*Customer, bool) { | |
select { | |
case c, ok := <-r.seats: | |
log.Println(c.Name, "called in from the waiting room") | |
// there was a customer waiting | |
return c, ok | |
default: | |
log.Println("no one in the waiting room") | |
// no one waiting | |
return nil, false | |
} | |
} | |
//////// | |
type Shop struct { | |
barbers []*Barber | |
waitingRoom *WaitingRoom | |
} | |
func NewShop(numChairs, numBarbers int) *Shop { | |
s := &Shop{ | |
barbers: make([]*Barber, 0, numBarbers), | |
waitingRoom: NewWaitingRoom(numChairs), | |
} | |
for i := 0; i < numBarbers; i++ { | |
b := NewBarber() | |
go b.Work(s.waitingRoom) | |
} | |
return s | |
} | |
func (s *Shop) GetHaircut(c *Customer) { | |
atomic.AddUint32(&numEntered, 1) | |
// check the barbers | |
for _, b := range s.barbers { | |
if b.Checkin(c) { | |
// got a cut right away! | |
return | |
} | |
} | |
if s.waitingRoom.FindSeat(c) { | |
// got a seat to wait | |
return | |
} | |
c.Leave() | |
} | |
//////// | |
var ( | |
numEntered, numCuts, numLeft uint32 | |
) | |
func statsRunner() { | |
for range time.Tick(time.Second * 5) { | |
e := atomic.LoadUint32(&numEntered) | |
c := atomic.LoadUint32(&numCuts) | |
l := atomic.LoadUint32(&numLeft) | |
log.Printf("STATS: %d entered, %d left, %d cuts", e, l, c) | |
} | |
} | |
func main() { | |
numChairs := flag.Int("c", 4, "`number` of chairs in waiting room") | |
numBarbers := flag.Int("b", 1, "`number` of barbers") | |
flag.Parse() | |
s := NewShop(*numChairs, *numBarbers) | |
/////////// | |
go statsRunner() | |
for { | |
// 0-5s random entry times | |
dur := time.Second * time.Duration(rand.Intn(5)) | |
time.Sleep(dur) | |
c := NewCustomer() | |
go s.GetHaircut(c) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
// extracted from https://blogs.ancestry.com/cm/whats-the-most-popular-surname-in-your-state/ | |
var surNames = []string{"Anderson", "Brown", "Chavez", "Garcia", "Hernandez", | |
"Johnson", "Jones", "Kim", "Lee", "Lopez", "Martinez", "Miller", "Nelson", | |
"Olson", "Smith", "Sullivan", "Williams", "Wong"} | |
// top 25 male and female names from 2000 | |
// extracted from http://www.babynames.it/top100/1/year-2000.html | |
var firstNames = []string{ | |
"Jacob", "Emily", // 1st | |
"Michael", "Hannah", // 2nd | |
"Matthew", "Madison", // 3rd | |
"Joshua", "Ashley", // 4th | |
"Christopher", "Sarah", // 5th | |
"Nicholas", "Alexis", // 6th | |
"Andrew", "Samantha", // 7th | |
"Joseph", "Jessica", // 8th | |
"Daniel", "Taylor", // 9th | |
"Tyler", "Elizabeth", // 10th | |
"William", "Lauren", // 11th | |
"Brandon", "Alyssa", // 12th | |
"Ryan", "Kayla", // 13th | |
"John", "Abigail", // 14th | |
"Zachary", "Brianna", // 15th | |
"David", "Olivia", // 16th | |
"Anthony", "Emma", // 17th | |
"James", "Megan", // 18th | |
"Justin", "Grace", // 19th | |
"Alexander", "Victoria", // 20th | |
"Jonathan", "Rachel", // 21st | |
"Christian", "Anna", // 22nd | |
"Austin", "Sydney", // 23rd | |
"Dylan", "Destiny", // 24th | |
"Ethan", "Morgan", // 25th | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment