GopherCon 2018 - Rethinking Classic Concurrency Patterns

conference, golang, gophercon2018, notes

These are some notes from my experiences at the GopherCon 2018. I don’t expect these will be laid out in any particularly useful way; I am mostly taking them so I can remember some of the bits I found most useful in the future.

Concurrency Patterns

  • In Go

    • Start goroutines when you have concurrent work
    • Share by communicating
  • Others

    • Futures
    • Queues
  • Concurrency is not //-ism

  • Concurrency is not async

Async APIs

  • Callbacks

  • Futures (return object that allows later waiting)

    • like single-element buffered channel
    • but has traps
  • Producer/Consumer queue

    • like unbuffered channel that can retrieve many results

Async Benefits

  • Responsiveness
  • Efficiency

Async Drawbacks

  • No clarity on error conditions, cancellation, blocking vs. nonblocking, etc.

Condition Variables / Monitors

  • Wait, Signal, Broadcast for a queue

  • Drawbacks

    • Spurious wakeups (too many, wrong worker)
    • Forgotten signals
    • Starvation
    • Unresponsive cancellation
    • Big Idea: issue is with sharing memory

Better Ways

  • Let the caller use concurrency, don’t build it in

    • Call sites are much easier to understand
  • Share by communicating

    • Send data over a channel (even a pool – buffered channel limit tokens)

Case Study: Worker Pool

  • Start a set of workers

  • Another goroutine sends tasks to worker and blocks until a worker is available

  • Benefits

    • Limit work in flight
  • Drawbacks

    • Can leak workers forever
    • Workers can be idle arbitrarily long
    • Still have resource bosts

Rethinking Worker Pools

  • Start goroutines when there is work, and exit after

    • WaitGroup, acquire token in semaphore channel BEFORE go
    • Can even skip the WaitGroup in some situations – add a loop to push N sem tokens onto the semaphore channel after the work should be done