Go Concurrency
November 19, 2022
I’ve had a difficult time internalizing Go’s concurrency primitives. They’re really rather easy to understand but I get confused on basic things regularly and have to dig through many how to guides to figure things out.
Creating a Go Routine
You create a “go routine” using the go keyword. The go routine will
create a thread into the go virtual machine and start executing.
A go routine can be a function or closure. There needs to be some caution used when the go routine is created as a closure as it can have sometimes unsafe access to variables outside its closure block.
Synchronization: sync.WaitGroup
To synchronize go routines typically the sync.WaitGroup type is used. The waitgroup
is basically a mutex protected counter that contains three key methods
Add(delta)this increments (or deciments) the waitgroup counter by deltaDone()this decrements the waitgroup counter by 1. Its actually just a convenience wraper for Add(-1)Wait()- A blocking call that won’t exit unil the waitgroup counter is 0 (like thread.Join)
Typically a pointer to a waitgroup and a reference is passed to each go routine.
Communication - channels
So go routines cannot “return” data to the main thread. So typically what is used to communicate data between go routines is the “channel” built in type.
Go routines can place data into a channel and read data out of channel via the -> and <-
operators. Channels are created via the builin make function ex: ch := make(chan int) ch
is a channel of type int.
ch <- 10put integer 10 into the int channel chmyInt := <- ch- reach 10 out of the int channel ch
It is best to close channels with the close() function as stray channels will leak memory
On thing that confused me early on is both reading out of a channel and writing to a unbuffered channel is a blocking operation. So for every go routine that is inputting to a unbuffered channel you need another goroutine selecting the data out of the channel. Then you need a third go routine to close the channel to terminate the communication.
One fanout type system you can create a waitgroup and a channel to read and write to. One go routine will write to the channel, before spawning each go routine you will increment the wg. Typically the results will be joined in the main channel and a third go routine will close the channel while a third go routine will call wait on the wg and then close the channel.
Other Documentation and Information
There’s an excellent presentation on how concurrency can interact with the garbage collector and how to profile it with pprof here the video is rather long but its a nice simple instructional video.