Slide 1

Slide 1 text

Applied concurrency in Go November 9, 2015 · PARIS Matt Aimonetti

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

web APIs Data encoders / decoders MySQL Async processor (copy/move files, analyze, decode/encode/transcode, report…) Distributed Queue Messaging Elastic Search Redis Memcached Cloud Storage

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Jing Li http://www.jinglidesign.com/

Slide 7

Slide 7 text

var logs = map[time.Time]string{} type order struct { dish string num int duration time.Duration } type chef struct { station int name string busyState bool mutex *sync.Mutex }

Slide 8

Slide 8 text

func newChef(name string, station int) *chef { return &chef{ station: station, name: name, mutex: &sync.Mutex{}} }

Slide 9

Slide 9 text

func (c *chef) cook(o *order) { if c.isBusy() { for c.isBusy() { time.Sleep(10 * time.Millisecond) } } c.busy(true) fmt.Printf("\t%s is cooking a %s (order %d)\n", c.name, o.dish, o.num) time.Sleep(o.duration) logs[time.Now()] = fmt.Sprintf("%s cooked a %s", c.name, o.dish) c.rest() }

Slide 10

Slide 10 text

func (c *chef) rest() { // minimal rest required by law fmt.Printf("\t%s is resting\n", c.name) time.Sleep(100 * time.Millisecond) c.busy(false) }

Slide 11

Slide 11 text

func (c *chef) isBusy() bool { defer c.mutex.Unlock() c.mutex.Lock() return c.busyState } func (c *chef) busy(b bool) { c.mutex.Lock() c.busyState = b c.mutex.Unlock() }

Slide 12

Slide 12 text

func main() { chefs := []*chef{newChef("François", 2), newChef("Rose", 3)} orders := []*order{ {"Blanquette de veau", 1, 1500 * time.Millisecond}, {"Soupe à l'oignon", 2, 850 * time.Millisecond}, // […] } startT := time.Now() fire(orders, chefs) fmt.Printf("all done in %s, closing the kitchen\n", time.Since(startT)) fmt.Println("logs:") for t, entry := range logs { fmt.Printf("%s: %s\n", t, entry) } }

Slide 13

Slide 13 text

func fire(orders []*order, chefs []*chef) { for _, order := range orders { for _, chef := range chefs { if !chef.isBusy() { chef.cook(order) break } else { fmt.Println(".") } } } } 1

Slide 14

Slide 14 text

François Rose Closing Kitchen

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

$ go run chezRenée.go

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

func fire(orders []*order, chefs []*chef) { for _, order := range orders { for _, chef := range chefs { if !chef.isBusy() { break } else { fmt.Println(".") } } } } chef.cook(order) 2

Slide 19

Slide 19 text

$ go run chezRenée.go

Slide 20

Slide 20 text

func three(orders []*order, chefs []*chef) { wg := &sync.WaitGroup{} for _, order := range orders { outterLoop: for { for _, chef := range chefs { if !chef.isBusy() { chef.cookAndYell(order, wg) break outterLoop } } } } wg.Wait() } 3

Slide 21

Slide 21 text

func (c *chef) cookAndYell(o *order, wg *sync.WaitGroup) { wg.Add(1) if c.isBusy() { for c.isBusy() { time.Sleep(10 * time.Millisecond) } } c.busy(true) go func() { fmt.Printf("\t%s is cooking a %s (order %d)\n", c.name, o.dish, o.num) time.Sleep(o.duration) logs[time.Now()] = fmt.Sprintf("%s cooked a %s", c.name, o.dish) c.restAndYell(wg) }() } 3

Slide 22

Slide 22 text

func (c *chef) restAndYell(wg *sync.WaitGroup) { // minimal rest required by law fmt.Printf("\t\t*%s is resting*\n", c.name) time.Sleep(100 * time.Millisecond) c.busy(false) wg.Done() } 3

Slide 23

Slide 23 text

func three(orders []*order, chefs []*chef) { wg := &sync.WaitGroup{} for _, order := range orders { outterLoop: for { for _, chef := range chefs { if !chef.isBusy() { chef.cookAndYell(order, wg) break outterLoop } } } } wg.Wait() } 3

Slide 24

Slide 24 text

$ go run chezRenée.go

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

func fire(orders []*order, chefs []*chef) { orderWheel := make(chan *order) for _, c := range chefs { go func(c *chef) { for o := range orderWheel { c.cook(o) } }(c) } for i, order := range orders { fmt.Printf("order %d: %s ", i, order.dish) orderWheel <- order } close(orderWheel) } 4

Slide 27

Slide 27 text

type chef struct { station int name string busyState bool mutex *sync.Mutex } lock free func (c *chef) isBusy() bool { defer c.mutex.Unlock() c.mutex.Lock() return c.busyState } func (c *chef) busy(b bool) { c.mutex.Lock() c.busyState = b c.mutex.Unlock() }

Slide 28

Slide 28 text

$ go run chezRenée.go

Slide 29

Slide 29 text

func five(orders []*order, chefs []*chef) { wg := &sync.WaitGroup{} orderChan := make(chan *order) for _, c := range chefs { wg.Add(1) go func(c *chef) { for o := range orderChan { c.cook(o) } wg.Done() }(c) } for i, order := range orders { fmt.Printf("order %d: %s ", i, order.dish) orderChan <- order } close(orderChan) wg.Wait() } 5

Slide 30

Slide 30 text

$ go run chezRenée.go

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

Data Race Detector $ go run -race chezRenée.go

Slide 33

Slide 33 text

================== WARNING: DATA RACE Write by goroutine 7: runtime.mapassign1() /Users/mattetti/go/src/runtime/hashmap.go:411 +0x0 main.(*chef).cook() /Users/mattetti/Code/talks/dotGo/main.go:80 +0x7ff main.five.func1() /Users/mattetti/Code/talks/dotGo/main.go:278 +0xa6 Previous write by goroutine 8: runtime.mapassign1() /Users/mattetti/go/src/runtime/hashmap.go:411 +0x0 main.(*chef).cook() /Users/mattetti/Code/talks/dotGo/main.go:80 +0x7ff main.five.func1() /Users/mattetti/Code/talks/dotGo/main.go:278 +0xa6 Goroutine 7 (running) created at: main.five() /Users/mattetti/Code/talks/dotGo/main.go:280 +0x17d main.main() /Users/mattetti/Code/talks/dotGo/main.go:142 +0xee7 Goroutine 8 (running) created at: main.five() /Users/mattetti/Code/talks/dotGo/main.go:280 +0x17d main.main() /Users/mattetti/Code/talks/dotGo/main.go:142 +0xee7 ==================

Slide 34

Slide 34 text

func (c *chef) cook(o *order) { fmt.Printf("\t%s is cooking %s (order %d)\n", c.name, o.dish, o.num) time.Sleep(o.duration) logs[time.Now()] = fmt.Sprintf("%s cooked %s", c.name, o.dish) c.rest() }

Slide 35

Slide 35 text

logs[time.Now()] = "afterparty at 18:45" @mattetti

Slide 36

Slide 36 text

No content

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

No content

Slide 39

Slide 39 text

@mattetti Merci! Matt Aimonetti