Back to articles
Go 12 min read

Go Concurrency Patterns

From goroutines to pipelines — mastering concurrency in Go for production systems.

SE
SUNSTAR Engineering Team
Apr 28, 2026

Understanding Goroutines

Goroutines are lightweight threads managed by the Go runtime. They start with only 2KB of stack space, making it feasible to run thousands concurrently.

Channel Patterns

Fan-Out, Fan-In

Distribute work across multiple goroutines and collect results:

func fanOut(in <-chan int, workers int) []<-chan int {
    channels := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        channels[i] = worker(in)
    }
    return channels
}

func fanIn(channels []<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    for _, c := range channels {
        wg.Add(1)
        go func(ch <-chan int) {
            defer wg.Done()
            for v := range ch {
                out <- v
            }
        }(c)
    }
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}

Pipeline Pattern

Chain stages connected by channels:

func pipeline(done <-chan struct{}, nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            select {
            case out <- n:
            case <-done:
                return
            }
        }
    }()
    return out
}

Error Handling

Use a result type to combine values with errors in concurrent operations:

type Result struct {
    Value interface{}
    Err   error
}

Context Propagation

Always pass context through your concurrent pipelines to enable cancellation and deadlines.

Conclusion

Mastering these concurrency patterns is essential for building robust, scalable Go applications.