Go > Concurrency > Concurrency Patterns > Fan-out and fan-in
Fan-Out and Fan-In in Go
Demonstrates the fan-out and fan-in concurrency pattern in Go, enabling parallel processing of tasks and aggregation of results.
Introduction to Fan-Out, Fan-In
Fan-out
is a concurrency pattern where multiple goroutines perform work delegated to them from a single goroutine. Fan-in
is a pattern where multiple goroutines send their results to a single goroutine for aggregation or processing. Together, they allow for parallel execution and efficient resource utilization.
Code Implementation
This code demonstrates the fan-out and fan-in pattern. The main
function creates a channel for jobs and a channel for results. It then launches multiple worker
goroutines (fan-out). Each worker reads jobs from the jobs
channel, performs some work (in this case, multiplying the job number by 2), and sends the result to the results
channel. The main
function then sends jobs to the jobs
channel and closes it. A separate goroutine waits for all workers to complete using a sync.WaitGroup
. Once all workers are done, it closes the results
channel, signaling that no more results will be sent. Finally, the main
function iterates over the results
channel and prints the results (fan-in).
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Printf("worker:%d started job:%d\n", id, j)
// Simulate work
// time.Sleep(time.Second)
results <- j * 2
fmt.Printf("worker:%d finished job:%d\n", id, j)
}
}
func main() {
numJobs := 10
numWorkers := 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
// Fan-out: Launch multiple workers
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 1; i <= numWorkers; i++ {
go func(i int) {
defer wg.Done()
worker(i, jobs, results)
}(i)
}
// Send jobs
for i := 1; i <= numJobs; i++ {
jobs <- i
}
close(jobs)
// Fan-in: Collect results
go func() {
wg.Wait()
close(results)
}()
// Print results
for a := range results {
fmt.Println(a)
}
}
Concepts Behind the Snippet
The core concept involves distributing work across multiple concurrent workers (goroutines) to speed up processing. Fan-out distributes the work, and fan-in collects and aggregates the results. This is essential for parallelizing tasks that can be divided into smaller independent units.
Real-Life Use Case
Imagine processing a large batch of images. Each image can be processed independently. Fan-out can be used to distribute the image processing tasks to multiple goroutines. Fan-in can then collect the processed images (or their results, like metadata) into a single stream for further processing or storage. Another use case could be a web server handling incoming requests. Each request can be handled by a worker goroutine, allowing the server to handle multiple requests concurrently.
Best Practices
Interview Tip
Be prepared to discuss the advantages and disadvantages of concurrency patterns like fan-out and fan-in. Explain how these patterns help improve performance and resource utilization, but also highlight the complexities of managing concurrency, such as race conditions and deadlocks. Knowing how to use sync.WaitGroup
and channels is crucial.
When to Use Them
Use fan-out and fan-in when you have a task that can be divided into smaller, independent subtasks that can be executed in parallel. This is particularly beneficial when dealing with I/O-bound or CPU-bound tasks where parallel processing can significantly reduce execution time.
Memory Footprint
The memory footprint depends on the number of goroutines launched and the size of the data being processed. Launching too many goroutines can lead to increased memory consumption and context switching overhead. Carefully consider the number of workers based on available resources.
Alternatives
Alternatives include using thread pools or libraries like ants
that provide more sophisticated concurrency management features. For simpler tasks, using a fixed number of goroutines with a shared channel might suffice. Consider using the errgroup
package for enhanced error handling and context propagation in concurrent operations.
Pros
Cons
FAQ
-
What is the difference between concurrency and parallelism?
Concurrency is the ability to deal with multiple things at once. Parallelism is the ability to do multiple things at the same time. Concurrency is about structure, while parallelism is about execution. You can have concurrency without parallelism, but not parallelism without concurrency. -
How do I limit the number of worker goroutines?
Use a worker pool pattern. Create a fixed-size channel for workers and launch a fixed number of goroutines that read from this channel. This limits the number of concurrent workers. -
How do I handle errors in worker goroutines?
Use a channel to collect errors from the worker goroutines. Each worker can send any errors it encounters to this channel. The main goroutine can then monitor this channel and handle the errors accordingly. Consider using theerrgroup
package for a more robust error handling solution.