Applied concurrency in Go

Applied concurrency in Go

Go makes concurrency & parallelism easier, but you still need to learn a few new idioms. This talk shows how to write proper concurrent code in Go by showing you common mistakes.

C69521d6e22fc0bbd69337ec8b1698df?s=128

Matt Aimonetti

November 10, 2015
Tweet

Transcript

  1. Applied concurrency in Go November 9, 2015 · PARIS Matt

    Aimonetti
  2. None
  3. 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
  4. None
  5. None
  6. Jing Li http://www.jinglidesign.com/

  7. 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 }
  8. func newChef(name string, station int) *chef { return &chef{ station:

    station, name: name, mutex: &sync.Mutex{}} }
  9. 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() }
  10. 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) }
  11. 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() }
  12. 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) } }
  13. 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
  14. François Rose Closing Kitchen

  15. None
  16. $ go run chezRenée.go

  17. None
  18. 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
  19. $ go run chezRenée.go

  20. 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
  21. 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
  22. 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
  23. 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
  24. $ go run chezRenée.go

  25. None
  26. 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
  27. 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() }
  28. $ go run chezRenée.go

  29. 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
  30. $ go run chezRenée.go

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

  33. ================== 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 ==================
  34. 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() }
  35. logs[time.Now()] = "afterparty at 18:45" @mattetti

  36. None
  37. None
  38. None
  39. @mattetti Merci! Matt Aimonetti