Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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.

Matt Aimonetti

November 10, 2015
Tweet

More Decks by Matt Aimonetti

Other Decks in Programming

Transcript

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

    station, name: name, mutex: &sync.Mutex{}} }
  4. 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() }
  5. 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) }
  6. 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() }
  7. 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) } }
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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() }
  16. 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
  17. ================== 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 ==================
  18. 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() }