Best Practices
Goroutine Management Principles
1. Controlled Goroutine Creation
func controlledGoroutines(items []int) {
maxWorkers := runtime.NumCPU()
sem := make(chan struct{}, maxWorkers)
for _, item := range items {
sem <- struct{}{}
go func(val int) {
defer func() { <-sem }()
processItem(val)
}(item)
}
}
Concurrency Patterns
graph TD
A[Concurrency Patterns] --> B[Worker Pools]
A --> C[Fan-Out/Fan-In]
A --> D[Context Cancellation]
A --> E[Semaphore]
Resource Management Strategies
Strategy |
Description |
Benefit |
Context Cancellation |
Propagate cancellation signals |
Prevent resource leaks |
WaitGroup |
Synchronize goroutine completion |
Ensure clean shutdown |
Channels |
Communicate between goroutines |
Thread-safe data exchange |
Error Handling in Goroutines
func robustGoroutineExecution(tasks []Task) error {
errChan := make(chan error, len(tasks))
var wg sync.WaitGroup
for _, task := range tasks {
wg.Add(1)
go func(t Task) {
defer wg.Done()
if err := t.Execute(); err != nil {
errChan <- err
}
}(task)
}
go func() {
wg.Wait()
close(errChan)
}()
return collectErrors(errChan)
}
Timeout and Cancellation
func timeoutOperation(ctx context.Context) error {
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
resultChan := make(chan Result, 1)
go func() {
result := performLongRunningTask()
resultChan <- result
}()
select {
case result := <-resultChan:
return processResult(result)
case <-ctx.Done():
return ctx.Err()
}
}
Goroutine Pool Implementation
type WorkerPool struct {
tasks chan func()
workers int
}
func NewWorkerPool(workerCount int) *WorkerPool {
pool := &WorkerPool{
tasks: make(chan func()),
workers: workerCount,
}
pool.start()
return pool
}
func (p *WorkerPool) start() {
for i := 0; i < p.workers; i++ {
go func() {
for task := range p.tasks {
task()
}
}()
}
}
Common Anti-Patterns to Avoid
- Creating unbounded goroutines
- Forgetting to close channels
- Ignoring goroutine errors
- Blocking main goroutine unnecessarily
LabEx Recommended Practices
- Use context for cancellation
- Implement graceful shutdown
- Monitor goroutine lifecycle
- Use buffered channels wisely
- Limit concurrent operations
Advanced Synchronization
type SafeCounter struct {
mu sync.Mutex
counters map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.counters[key]++
}
Key Takeaways
- Prioritize controlled concurrency
- Use built-in synchronization primitives
- Implement proper error handling
- Design for predictable resource management
By following these best practices, developers can create efficient, reliable, and performant concurrent applications in their LabEx Go projects.