Go uses goroutines for concurrency. Simplifying, goroutines are like threads.
Gorutines execute at the same time and share memory.
Since goroutines can read / write the same memory at the same time, you have to ensure exclusive access e.g. by using a mutex.
To coordinate work between goroutines Go provides channels, which are thread-safe queues.
Here’s an example of using worker pool of goroutines and coordinating their work with channels:
https://codeeval.dev/gist/2f155938fdd0d264618bcd8b38b091b6
There’s a lot to unpack here.
We launch 2 workers with go sqrtWorker(chIn, chOut)
.
Each sqrtWorker
function is running independently and concurrently with all other code.
We use a single channel of int values to queue work items to be processed by worker goroutines using <-
send operation on a channel.
Most of the time it’s not safe to access the same variable from multiple goroutines. Channels and sync.WaitGroup
are exceptions.
Worker goroutines sqrtWorker
pick up values from the channel using range
.
We don’t know which worker will pick any given value.
Worker’s for
loop terminates when chIn
is closed with close(chIn)
and worker goroutine terminates.
To pass results of worker goroutines back to the caller we use another channel.
In this example we use unbuffered channels which only have capacity for one item at a time. For that reason we launch another goroutine to fill chIn
. Otherwise we would risk dead-lock.
To shutdown workers we close the chIn
.
We then wait for results created by workers by iterating on chOut
.
There’s one more complication. Unless we close chOut
, the for sqrt := range chOut
loop will wait forever.
To stop the loop, we need to close(chOut)
but when to do it?