Practical Examples
Real-World Channel Applications
Channels are powerful tools for solving complex concurrency problems. This section explores practical scenarios demonstrating effective channel usage.
1. Worker Pool Pattern
func workerPool(jobs <-chan int, results chan<- int, workerCount int) {
for i := 0; i < workerCount; i++ {
go func() {
for job := range jobs {
// Process job
results <- job * 2
}
}()
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
workerPool(jobs, results, 5)
// Send jobs
for i := 0; i < 50; i++ {
jobs <- i
}
close(jobs)
// Collect results
for i := 0; i < 50; i++ {
<-results
}
}
Concurrency Flow Visualization
graph TD
A[Job Queue] -->|Distribute| B[Worker 1]
A -->|Tasks| C[Worker 2]
A -->|Concurrently| D[Worker 3]
B --> E[Results Channel]
C --> E
D --> E
2. Timeout Handling
func timeoutExample() {
ch := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
ch <- "Completed"
}()
select {
case result := <-ch:
fmt.Println(result)
case <-time.After(1 * time.Second):
fmt.Println("Operation timed out")
}
}
Channel Usage Patterns
Pattern |
Description |
Use Case |
Worker Pool |
Distribute tasks across multiple workers |
Parallel processing |
Timeout |
Limit operation duration |
Preventing hanging |
Fan-Out/Fan-In |
Multiple producers, single consumer |
Complex data aggregation |
3. Fan-Out/Fan-In Pattern
func fanOutFanIn(input <-chan int) <-chan int {
numWorkers := 3
intermediateChannels := make([]<-chan int, numWorkers)
// Fan-out
for i := 0; i < numWorkers; i++ {
intermediateChannels[i] = worker(input)
}
// Fan-in
return merge(intermediateChannels...)
}
func worker(input <-chan int) <-chan int {
output := make(chan int)
go func() {
for num := range input {
output <- num * num
}
close(output)
}()
return output
}
func merge(channels ...<-chan int) <-chan int {
var wg sync.WaitGroup
mergedCh := make(chan int)
output := func(ch <-chan int) {
defer wg.Done()
for v := range ch {
mergedCh <- v
}
}
wg.Add(len(channels))
for _, ch := range channels {
go output(ch)
}
go func() {
wg.Wait()
close(mergedCh)
}()
return mergedCh
}
4. Cancellation with Context
func cancelableOperation(ctx context.Context) error {
ch := make(chan int, 1)
go func() {
// Simulate long-running task
select {
case <-ctx.Done():
return
case ch <- performTask():
close(ch)
}
}()
select {
case <-ctx.Done():
return ctx.Err()
case result, ok := <-ch:
if !ok {
return nil
}
// Process result
return nil
}
}
Advanced Considerations
- Use buffered channels for performance optimization
- Implement proper error handling
- Close channels to prevent resource leaks
- Leverage context for cancellation
Error Handling Strategies
func robustChannelOperation() error {
ch := make(chan int, 5)
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
close(ch)
}()
// Complex channel operations
return nil
}
Best Practices
- Choose appropriate buffer sizes
- Use select for non-blocking operations
- Implement proper goroutine lifecycle management
- Handle potential deadlocks
LabEx recommends practicing these patterns to master Go's concurrent programming techniques.