Go Concurrency Series: Concurrency Patterns
Dive into common concurrency patterns adopted in Golang, like the Worker Pool Pattern and the Pipeline Pattern.
Let’s continue being a little more hands-on in our Go Concurrency Series! In this post, we’ll look into the implementation side and cover the common concurrency patterns that can be utilised in Golang.
Concurrency in Go is based on the 'CSP' (Communicating Sequential Processes) model, which emphasizes communication between independent processes (goroutines) by sending events(data) through channels.
So, keeping that in mind, let’s see some common concurrency patterns we can use in Go.
Worker Pool
In the Worker Pool Pattern, a fixed number of workers (goroutines) are created to process jobs. These workers fetch tasks from a queue(channel) and process them concurrently. This pattern is especially useful for managing resource utilization, as it prevents spawning an excessive number of goroutines, which could lead to higher memory usage and reduced performance.
Remember that since costs of creating goroutines is relatively cheaper, you might not always see an advantage of using worker pool, so use benchmarking to see if it works for you!
Advantages of the Worker Pool Pattern
Controlled Concurrency: Offers control over how many tasks run concurrently.
Load Distribution: Evenly distributes tasks across multiple workers.
Pipeline Pattern
The Pipeline Pattern is inspired by the concept of assembly lines in manufacturing, where a product moves through various stages, each adding something to the final product. In Go, this is implemented using channels and goroutines:
Stages: Each stage in the pipeline is a group of goroutines performing the same function. A stage takes input from an inbound channel, processes it, and sends it to the next stage through an outbound channel.
Channels: Channels are used to connect these stages. They allow for safe and synchronized communication between different stages in the pipeline.
Data Flow: Data flows through the pipeline from the first stage to the last, getting transformed/operated on at each stage.
The great part about pipeline pattern is that we can have different numbers of concurrent workers at each stage and we can define it based on the complexity of the task!
Advantages of the Pipeline Pattern
Modularity: Each stage is independent and encapsulates its processing logic, making the code more readable and maintainable.
Concurrency: Since each stage can run in parallel, the pattern can efficiently utilize multi-core processors.
Flexibility: It is easy to add, remove, or modify stages without affecting the entire pipeline.
In our next article, we’ll cover Fan-In and Fan-Out Patterns and delve into how we can explicitly cancel workers/stages when something goes wrong.
References:
Great code examples. I wish Substack had a proper highlighting. For our newsletter I can't decide: use something like carbon.sh (nice looking, but no copy) or just a code block.
But you have a lot of good code that people may want to try it out, so IMO code block would be better.