- https://golang.org/doc/effective_go.html#concurrency
- https://talks.golang.org/2012/waza.slide
- https://blog.golang.org/pipelines
Spec sections on channels (https://golang.org/ref/spec#Channel_types), send (https://golang.org/ref/spec#Send_statements), receive (https://golang.org/ref/spec#Receive_operator), select (https://golang.org/ref/spec#Select_statements), close (https://golang.org/ref/spec#Close). The memory model is described at https://golang.org/ref/mem.
- http://blog.golang.org/go-concurrency-patterns-timing-out-and
- http://blog.golang.org/context
- http://blog.golang.org/race-detector
- http://blog.golang.org/concurrency-is-not-parallelism
- http://blog.golang.org/advanced-go-concurrency-patterns
Running a concurrent test under the race detector is a useful check. Helps to get a little familiarity with the sync package: https://godoc.org/sync. Introductory stuff mostly emphasizes channels, because that's what's specific to Go, but when your goal isn't communication you should be aware of, e.g. sync.WaitGroup
to wait for workers to finish, sync.Mutex
or sync.RWLock
when you're really just sharing data, sync.Once
during init, sync/atomic
in the somewhat rarer cases you have global state you can update atomically.
For performance, if you have tiny tasks (where microseconds of overhead per task matters), using buffers or batching them can help; it's rarely an issue when network or DB operations are involved, but can be on small pure-computation tasks. I ran into this in some parallel sorting code (and dealt by only sending sorts of 128+ items to other sort workers). Standard library source also has some concurrency management--it's usually readable code, though sometimes you have to understand what it's doing to understand what the concurrency primitives are there for.
I hate to say this but in learning this stuff there's often a certain amount of trial and error, in getting to elegance if not to correctness; I think my first largish Go program accidentally implemented a lock using a buffered channel. It may help to open a text file somewhere to sketch out your goroutines, channels, etc. (separated from all the other details of your app), why you need each, and see if you can remove anything or change something to a better-fitting primitive.
Couple third-party things I haven't actually looked at:
- https://blog.gopheracademy.com/composable-pipelines-pattern/
- https://gobyexample.com/channels and the next few pages in the series