
Go Concurrency
Apply Effective Go concurrency patterns—reply channels and CPU-bound parallelization—when implementing Go services and workers.
Overview
Go Concurrency is an agent skill for the Build phase that teaches advanced Effective Go patterns for channel multiplexing and CPU-bound parallelization.
Install
npx skills add https://github.com/cxuu/golang-skills --skill go-concurrencyWhat is this skill?
- Channels-of-channels pattern with per-request resultChan on Request structs
- Rate-limited parallel non-blocking RPC-style handler loop without mutexes
- CPU-bound parallelization across cores using sync.WaitGroup on Vector workloads
- Situational guidance: use when multiplexing request/response or splitting independent compute
- 2 advanced pattern sections: Channels of Channels and CPU-Bound Parallelization
Adoption & trust: 676 installs on skills.sh; 110 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are writing Go services and need idiomatic concurrency beyond goroutines-and-channels 101 without mutex-heavy RPC shapes.
Who is it for?
Intermediate Go backend authors optimizing throughput for RPC-like queues or embarrassingly parallel numeric/vector work.
Skip if: Beginners learning sync.Mutex basics or teams that only need context.Context cancellation patterns without channel multiplexing.
When should I use this skill?
Implementing Go backends that need request/response multiplexing via reply channels or CPU-bound parallelization with WaitGroup.
What do I get? / Deliverables
You implement reply-channel request structs and WaitGroup-parallelized work with clear client/server flow matching Effective Go guidance.
- Idiomatic Request/resultChan structures and handler loops
- WaitGroup-based parallel implementation sketches for independent work units
Recommended Skills
Journey fit
Build is canonical because the skill is implementation reference for Go backends, not deployment or growth work. Backend subphase fits RPC-style request queues, channel-of-channels reply routing, and sync.WaitGroup parallel numeric work.
How it compares
Depth reference for two advanced patterns—not a full Go concurrency course or golangci-lint checker skill.
Common Questions / FAQ
Who is go-concurrency for?
Solo builders shipping Go APIs, workers, or CLIs who already know goroutines and want Effective Go–aligned advanced patterns.
When should I use go-concurrency?
Use it in Build/backend when designing request/response multiplexing with embedded reply channels or parallelizing independent CPU-bound slices with sync.WaitGroup.
Is go-concurrency safe to install?
It is documentation-only procedural knowledge with no bundled executables—review the Security Audits panel on this page like any third-party skill source.
SKILL.md
READMESKILL.md - Go Concurrency
# Advanced Concurrency Patterns Detailed reference for advanced concurrency patterns from Effective Go. These patterns are situational — use when you need request/response multiplexing or CPU-bound parallelization. --- ## Channels of Channels > **Source**: Effective Go A channel is a first-class value that can be allocated and passed around like any other. A powerful pattern is embedding a **reply channel** inside a request struct, letting each client provide its own path for the answer: ```go type Request struct { args []int f func([]int) int resultChan chan int } ``` The client sends a request with a function, its arguments, and a channel on which to receive the result: ```go request := &Request{[]int{3, 4, 5}, sum, make(chan int)} clientRequests <- request fmt.Printf("answer: %d\n", <-request.resultChan) ``` The server handler reads from the queue and sends results back on each request's reply channel: ```go func handle(queue chan *Request) { for req := range queue { req.resultChan <- req.f(req.args) } } ``` This pattern forms the basis for a rate-limited, parallel, non-blocking RPC system without a mutex in sight. --- ## CPU-Bound Parallelization > **Source**: Effective Go (modernized) When a computation can be broken into independent pieces, parallelize it across CPU cores using a `sync.WaitGroup` to wait for completion: ```go type Vector []float64 func (v Vector) DoSome(i, n int, u Vector) { for ; i < n; i++ { v[i] += u.Op(v[i]) } } func (v Vector) DoAll(u Vector) { numCPU := runtime.NumCPU() var wg sync.WaitGroup wg.Add(numCPU) for i := 0; i < numCPU; i++ { go func(i int) { defer wg.Done() v.DoSome(i*len(v)/numCPU, (i+1)*len(v)/numCPU, u) }(i) } wg.Wait() } ``` Use `runtime.NumCPU()` for hardware cores or `runtime.GOMAXPROCS(0)` to honor the user's resource configuration. > **Important**: Don't confuse concurrency (structuring a program as > independently executing components) with parallelism (executing calculations > simultaneously on multiple CPUs). Go is a concurrent language; not all > parallelization problems fit its model. --- ## Common Mistakes ### Forgetting to signal completion If a goroutine never calls `wg.Done()` (or never sends on a done channel), the waiting goroutine blocks forever: ```go // Bad: Missing wg.Done — deadlocks var wg sync.WaitGroup wg.Add(1) go func() { doWork() }() wg.Wait() // Good: Always defer wg.Done var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() doWork() }() wg.Wait() ``` ### Unbounded goroutine spawning Launching one goroutine per work item with no limit can exhaust memory or overwhelm downstream resources. Use a semaphore to cap concurrency: ```go // Bad: Spawns len(items) goroutines at once var wg sync.WaitGroup for _, item := range items { wg.Add(1) go func(it Item) { defer wg.Done() process(it) }(item) } wg.Wait() // Good: Semaphore limits concurrency to maxWorkers var wg sync.WaitGroup sem := make(chan struct{}, maxWorkers) for _, item := range items { wg.Add(1) sem <- struct{}{} go func(it Item) { defer wg.Done() defer func() { <-sem }() process(it) }(item) } wg.Wait() ``` # Buffer Pooling with Channels Use a buffered channel as a free list to reuse allocated buffers, avoiding repeated allocations. This "leaky buffer" pattern uses `select` with `default` for non-blocking operations. > **Source**: Effective Go ```go var freeList = make(chan *Buffer, 100) // Buffered channel as free list // Client: Get buffer from free list or allocate new one func getBuffer() *Buffer { select { case b := <-freeList: return b // Reuse existing buffer default: return new(Buffer) // Free list empty; allocate new buffer } } // Server: Return buffer to free list if room, otherwise drop it func putBuffer(b *Buffer) {