Concurrency Patterns
Overview of Concurrency Patterns
Concurrency patterns in Go provide structured approaches to solving complex concurrent programming challenges using channels and goroutines.
Common Concurrency Patterns
graph TD
A[Concurrency Patterns] --> B[Worker Pool]
A --> C[Fan-Out/Fan-In]
A --> D[Semaphore]
A --> E[Generator]
1. Worker Pool Pattern
func workerPool(jobs <-chan int, results chan<- int) {
for job := range jobs {
results <- processJob(job)
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Create worker pool
for w := 1; w <= 3; w++ {
go workerPool(jobs, results)
}
// Send jobs
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// Collect results
for a := 1; a <= 5; a++ {
<-results
}
}
2. Fan-Out/Fan-In Pattern
func fanOutFanIn() {
// Distribute work across multiple goroutines
ch1 := generator(1, 2, 3, 4, 5)
ch2 := generator(6, 7, 8, 9, 10)
// Merge channels
fanIn := merge(ch1, ch2)
// Process merged results
for result := range fanIn {
fmt.Println(result)
}
}
func generator(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func merge(channels ...<-chan int) <-chan int {
var wg sync.WaitGroup
out := make(chan int)
output := func(c <-chan int) {
defer wg.Done()
for n := range c {
out <- n
}
}
wg.Add(len(channels))
for _, c := range channels {
go output(c)
}
go func() {
wg.Wait()
close(out)
}()
return out
}
3. Semaphore Pattern
type Semaphore struct {
semaphore chan struct{}
}
func NewSemaphore(max int) *Semaphore {
return &Semaphore{
semaphore: make(chan struct{}, max),
}
}
func (s *Semaphore) Acquire() {
s.semaphore <- struct{}{}
}
func (s *Semaphore) Release() {
<-s.semaphore
}
func main() {
sem := NewSemaphore(3)
for i := 0; i < 10; i++ {
go func(id int) {
sem.Acquire()
defer sem.Release()
// Perform limited concurrent work
fmt.Printf("Processing task %d\n", id)
}(i)
}
}
Concurrency Pattern Characteristics
Pattern |
Use Case |
Key Benefits |
Worker Pool |
Parallel task processing |
Controlled concurrency |
Fan-Out/Fan-In |
Distributing and collecting work |
Efficient resource utilization |
Semaphore |
Resource limiting |
Prevent system overload |
Advanced Considerations
- Use context for cancellation
- Implement proper error handling
- Manage goroutine lifecycles
Best Practices
- Choose the right pattern for your specific use case
- Minimize shared state
- Use channels for communication
- Avoid complex synchronization
At LabEx, we emphasize mastering these patterns to build robust concurrent applications in Go.