concepts: goroutines, blocking, and channels. The combination of these provides a versatile base for a variety of patterns. The Go standard library also provides a more conventional Mutex type, but I will not be discussing that here Go Concurrency Patterns 2 / 30
lightweight nature All goroutines run in the same address space. Go provides several methods to make this manageable, but it is still possible to create race conditions with insufficient care. Many operations block a goroutine, which will sleep until the blocking operation completes. Go Concurrency Patterns 3 / 30
between goroutines using channels A channel is a multireader, multiwriter threadsafe fixedlength queue Channels are typed and can transmit any Go type (including functions and other channels!). We write the type of a channel as chan T where T is the type it will transmit Go Concurrency Patterns 6 / 30
create a zerolength unbuffered channel. Writes to unbuffered channels will block until another goroutine reads the value Similarly, reads to unbuffered channels will block until another goroutine writes a value Go Concurrency Patterns 7 / 30
the channel repeatedly // until we close the channel for n := range input { fmt.Printf("Received: %d\n", n) } fmt.Println("Exiting receiver") } Go Concurrency Patterns 8 / 30
we can largely avoid blocking goroutines Each buffered channel has a length; if the number of values in the channel is less than that writes will not block Similarly, reads from buffered channels will only block if the channel is empty. Go Concurrency Patterns 10 / 30
without communication through shared channels or variables, it will proceed separately from the originating goroutine until it completes. This is used in HTTP handlers similarly to an async Express handler in Node.js Since network interactions and similar asynchronous actions block goroutines, we have a similar pattern without needing to explicitly await Go Concurrency Patterns 13 / 30
into a pipeline Each goroutine other than the first or last reads from one channel and writes to another. This is a special case of the channelgoroutine model's ability to create arbitrary dataflow graphs. Go Concurrency Patterns 18 / 30
i := 0; i < 20; i++ { // Read a single value from `sieveOutput` and assign to x x := <-sieveOutput newOutput := make(chan int) go sieveSingle(x, sieveOutput, newOutput) sieveOutput = newOutput fmt.Println(x) } } Go Concurrency Patterns 21 / 30
goroutine workers with a WaitGroup, which is provided in the Go standard library A WaitGroup provides a threadsafe counter and the ability for a goroutine to wait for it to reach 0 The basic WaitGroup pattern is to increment the counter as we spawn goroutines, decrement in each of those goroutines when they complete, and wait in the original goroutine for the others to complete Go Concurrency Patterns 23 / 30
0; i < 5; i++ { wg.Add(1) // we can create a goroutine with an IIFE go func(id int) { defer wg.Done() time.Sleep(5 * time.Second) results[id] = id + 4 }(i) } wg.Wait() fmt.Println(results) Go Concurrency Patterns 24 / 30
worker pools This can be useful when there's a need to control the total amount of work a given Go process takes on. One example is when consuming messages from Kafka; when uncontrolled it can easily overwhelm the program with work and starve each job to uselessness Go Concurrency Patterns 26 / 30
int, 5) resultsQueue := make(chan int) for i := 0; i < 5; i++ { go worker(workQueue, resultsQueue) } // Submit some work for x := 0; x < 10; x++ { workQueue <- x } Go Concurrency Patterns 28 / 30