Concurrency Patterns in Go | Part 2
In the part 1 we learned that channels are playing a crucial role in facilatating communication and sychronization between goroutines. While Go offers built-in channels that support complex synchronization patterns are required. In part 2, we will explore techniques that allow us to take this synchronization one step further.
The or-channel / The or-done-channel Pattern
To deal with more complex synchronization patterns, one such pattern is the Or-Channel, also known as the Or-Done-Channel. The Or-Channel is a powerful concept that allows you to combine multiple channels into a single channel, providing a way to wait for the first value or signal to be received from any of the input channels. It acts as a logical OR operator among the channels, ensuring that as soon as any channel sends a value or is closed, the Or-Channel receives that value or signal.
Use Case
Imagine a scenario where you want to perform multiple order requests simultaneously with pipeline pattern for each order (stage 1 (Validation Goroutine) , stage 2 (Enrichment Goroutine), and stage 3 (Calculation Goroutine)) and process accordinly all the responses that arrive. The Or-Channel allows you to achieve this behavior elegantly.
This pattern is particularly useful when you have these types multiple concurrent operations or goroutines running in parallel, and you need to wait for the first result or completion signal to second, and third respectively.
Let's try to explore by part one's example with simplified version.
Example
Assuming Order object resembles
Stage 1 (Validation)
Each order is received and validated for correctness and completeness. Any invalid orders are filtered out.
Stage 2 (Enrichment)
The valid orders are then enriched with additional information, such as customer details or product data, to enhance their content.
Stage 3 (Calculation)
The enriched orders are processed further to perform calculations, such as total order value or shipping costs.
Each stage is implemented as a separate goroutine, and channels are used to connect the stages. The orders flow through the pipeline, with each stage concurrently processing the orders it receives. This allows for parallelism, as multiple orders can be processed simultaneously by different stages.
In this code, the done channel is used to signal termination and gracefully shut down the concurrent stages of the pipeline.
Tee-Channel Pattern
The tee-channel pattern refers to a mechanism where a single channel is split into multiple channels to enable parallel processing of the data stream. It allows multiple consumers to receive the same data simultaneously.
Use Case
Imagine you have a system that receives a continuous stream of sensor data from multiple sensors distributed throughout a factory. You want to perform real-time analysis on this data and simultaneously store it for historical purposes. By utilizing a tee-channel, you can achieve this efficiently.
Here's how it would work:
To implement a tee-channel in Go, you can create a function that takes a source channel and multiple destination channels as arguments. Within this function, you can use a goroutine that loops over the source channel and sends the received values to all the destination channels.
The tee function takes a source channel source and a variadic argument destinations, which represents multiple destination channels. It launches a goroutine that continuously reads values from the source channel and sends them to each destination channel using a loop.Once the source channel is closed, the function closes all the destination channels to signal that no more values will be sent.
By using goroutines and channels, we achieve concurrent processing of the same data stream, allowing real-time analysis and storage to happen simultaneously.
Bridge-Channel Pattern
The bridge-channel pattern refers to connect or combine multiple channels into a single channel. It allows you to merge the streams of data from different channels into a unified stream, making it easier to consume and process the combined data.In this case, the bridge channel acts as a connector between multiple channels, enabling the flow of data between them.
Here's how it would work:
To implement a bridge channel in Go, you can create a function that takes multiple input channels and returns a single output channel.
The bridge function is called with sensorData1 and sensorData2 as arguments, which bridges these two input channels into a single output channel.
The code sets up two channels to send sensor data, bridges these channels into a single output channel, and consumes the values from the output channel concurrently using goroutines. The use of channels and goroutines allows for concurrent communication and processing of sensor data.
Conclusion
In conclusion, the use of channels in Go programming plays a crucial role in facilitating communication and synchronization between goroutines. In complex scenarios where advanced synchronization patterns are required, techniques such as the Or-Channel, Tee-Channel, and Bridge-Channel patterns can be used to enhance synchronization and parallel processing capabilities.
I'm constantly delighted to receive feedback. Whether you spot an error, have a suggestion for improvement, or just want to share your thoughts, please don't hesitate to comment/reach out. I truly value connecting with readers!