[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F1E3F4B7JS0VVZ185QJR8T":3,"skill-same:01M6F1E3F4B7JS0VVZ185QJR8T":68},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":34,"linkOnly":35,"markdownContent":36,"markdownBody":37,"frontMatter":38,"refName":39,"commitSha":40,"skillDirectory":41,"forksCount":42,"repositoryHtmlUrl":43,"repositoryHomepageUrl":44,"ownerHtmlUrl":45,"ownerBlogUrl":6,"ownerDescription":46,"updatedAt":47,"install":48,"resources":61},"01M6F1E3F4B7JS0VVZ185QJR8T","go-concurrency-patterns",null,"Master Go concurrency with goroutines, channels, sync primitives, and context. Use when building concurrent Go applications, implementing worker pools, or debugging race conditions.","ComeOnOliver\u002Fskillshub","ComeOnOliver","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F43215937?v=4","01M6E5JNFECJAAVFE0W7SMWWD2","Haoran Shu","01M6E5JNNDQS7AJZDYFQ8TPZCK",67,"MIT","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fgo-concurrency-patterns\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33],"go","concurrency","go concurrency patterns","patterns","sync","debugging","applications","context","primitives","use","master","worker","goroutines","channels","with","2026-11-10 10:12:30",false,"---\nname: go-concurrency-patterns\ndescription: Master Go concurrency with goroutines, channels, sync primitives, and context. Use when building concurrent Go applications, implementing worker pools, or debugging race conditions.\n---\n\n# Go Concurrency Patterns\n\nProduction patterns for Go concurrency including goroutines, channels, synchronization primitives, and context management.\n\n## When to Use This Skill\n\n- Building concurrent Go applications\n- Implementing worker pools and pipelines\n- Managing goroutine lifecycles\n- Using channels for communication\n- Debugging race conditions\n- Implementing graceful shutdown\n\n## Core Concepts\n\n### 1. Go Concurrency Primitives\n\n| Primitive | Purpose |\n|-----------|---------|\n| `goroutine` | Lightweight concurrent execution |\n| `channel` | Communication between goroutines |\n| `select` | Multiplex channel operations |\n| `sync.Mutex` | Mutual exclusion |\n| `sync.WaitGroup` | Wait for goroutines to complete |\n| `context.Context` | Cancellation and deadlines |\n\n### 2. Go Concurrency Mantra\n\n```\nDon't communicate by sharing memory;\nshare memory by communicating.\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"sync\"\n    \"time\"\n)\n\nfunc main() {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n\n    results := make(chan string, 10)\n    var wg sync.WaitGroup\n\n    \u002F\u002F Spawn workers\n    for i := 0; i \u003C 3; i++ {\n        wg.Add(1)\n        go worker(ctx, i, results, &wg)\n    }\n\n    \u002F\u002F Close results when done\n    go func() {\n        wg.Wait()\n        close(results)\n    }()\n\n    \u002F\u002F Collect results\n    for result := range results {\n        fmt.Println(result)\n    }\n}\n\nfunc worker(ctx context.Context, id int, results chan\u003C- string, wg *sync.WaitGroup) {\n    defer wg.Done()\n\n    select {\n    case \u003C-ctx.Done():\n        return\n    case results \u003C- fmt.Sprintf(\"Worker %d done\", id):\n    }\n}\n```\n\n## Patterns\n\n### Pattern 1: Worker Pool\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"sync\"\n)\n\ntype Job struct {\n    ID   int\n    Data string\n}\n\ntype Result struct {\n    JobID int\n    Output string\n    Err   error\n}\n\nfunc WorkerPool(ctx context.Context, numWorkers int, jobs \u003C-chan Job) \u003C-chan Result {\n    results := make(chan Result, len(jobs))\n\n    var wg sync.WaitGroup\n    for i := 0; i \u003C numWorkers; i++ {\n        wg.Add(1)\n        go func(workerID int) {\n            defer wg.Done()\n            for job := range jobs {\n                select {\n                case \u003C-ctx.Done():\n                    return\n                default:\n                    result := processJob(job)\n                    results \u003C- result\n                }\n            }\n        }(i)\n    }\n\n    go func() {\n        wg.Wait()\n        close(results)\n    }()\n\n    return results\n}\n\nfunc processJob(job Job) Result {\n    \u002F\u002F Simulate work\n    return Result{\n        JobID:  job.ID,\n        Output: fmt.Sprintf(\"Processed: %s\", job.Data),\n    }\n}\n\n\u002F\u002F Usage\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    jobs := make(chan Job, 100)\n\n    \u002F\u002F Send jobs\n    go func() {\n        for i := 0; i \u003C 50; i++ {\n            jobs \u003C- Job{ID: i, Data: fmt.Sprintf(\"job-%d\", i)}\n        }\n        close(jobs)\n    }()\n\n    \u002F\u002F Process with 5 workers\n    results := WorkerPool(ctx, 5, jobs)\n\n    for result := range results {\n        fmt.Printf(\"Result: %+v\\n\", result)\n    }\n}\n```\n\n### Pattern 2: Fan-Out\u002FFan-In Pipeline\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"sync\"\n)\n\n\u002F\u002F Stage 1: Generate numbers\nfunc generate(ctx context.Context, nums ...int) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for _, n := range nums {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n:\n            }\n        }\n    }()\n    return out\n}\n\n\u002F\u002F Stage 2: Square numbers (can run multiple instances)\nfunc square(ctx context.Context, in \u003C-chan int) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for n := range in {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n * n:\n            }\n        }\n    }()\n    return out\n}\n\n\u002F\u002F Fan-in: Merge multiple channels into one\nfunc merge(ctx context.Context, cs ...\u003C-chan int) \u003C-chan int {\n    var wg sync.WaitGroup\n    out := make(chan int)\n\n    \u002F\u002F Start output goroutine for each input channel\n    output := func(c \u003C-chan int) {\n        defer wg.Done()\n        for n := range c {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n:\n            }\n        }\n    }\n\n    wg.Add(len(cs))\n    for _, c := range cs {\n        go output(c)\n    }\n\n    \u002F\u002F Close out after all inputs are done\n    go func() {\n        wg.Wait()\n        close(out)\n    }()\n\n    return out\n}\n\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    \u002F\u002F Generate input\n    in := generate(ctx, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\n\n    \u002F\u002F Fan out to multiple squarers\n    c1 := square(ctx, in)\n    c2 := square(ctx, in)\n    c3 := square(ctx, in)\n\n    \u002F\u002F Fan in results\n    for result := range merge(ctx, c1, c2, c3) {\n        fmt.Println(result)\n    }\n}\n```\n\n### Pattern 3: Bounded Concurrency with Semaphore\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"golang.org\u002Fx\u002Fsync\u002Fsemaphore\"\n    \"sync\"\n)\n\ntype RateLimitedWorker struct {\n    sem *semaphore.Weighted\n}\n\nfunc NewRateLimitedWorker(maxConcurrent int64) *RateLimitedWorker {\n    return &RateLimitedWorker{\n        sem: semaphore.NewWeighted(maxConcurrent),\n    }\n}\n\nfunc (w *RateLimitedWorker) Do(ctx context.Context, tasks []func() error) []error {\n    var (\n        wg     sync.WaitGroup\n        mu     sync.Mutex\n        errors []error\n    )\n\n    for _, task := range tasks {\n        \u002F\u002F Acquire semaphore (blocks if at limit)\n        if err := w.sem.Acquire(ctx, 1); err != nil {\n            return []error{err}\n        }\n\n        wg.Add(1)\n        go func(t func() error) {\n            defer wg.Done()\n            defer w.sem.Release(1)\n\n            if err := t(); err != nil {\n                mu.Lock()\n                errors = append(errors, err)\n                mu.Unlock()\n            }\n        }(task)\n    }\n\n    wg.Wait()\n    return errors\n}\n\n\u002F\u002F Alternative: Channel-based semaphore\ntype Semaphore chan struct{}\n\nfunc NewSemaphore(n int) Semaphore {\n    return make(chan struct{}, n)\n}\n\nfunc (s Semaphore) Acquire() {\n    s \u003C- struct{}{}\n}\n\nfunc (s Semaphore) Release() {\n    \u003C-s\n}\n```\n\n### Pattern 4: Graceful Shutdown\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"os\"\n    \"os\u002Fsignal\"\n    \"sync\"\n    \"syscall\"\n    \"time\"\n)\n\ntype Server struct {\n    shutdown chan struct{}\n    wg       sync.WaitGroup\n}\n\nfunc NewServer() *Server {\n    return &Server{\n        shutdown: make(chan struct{}),\n    }\n}\n\nfunc (s *Server) Start(ctx context.Context) {\n    \u002F\u002F Start workers\n    for i := 0; i \u003C 5; i++ {\n        s.wg.Add(1)\n        go s.worker(ctx, i)\n    }\n}\n\nfunc (s *Server) worker(ctx context.Context, id int) {\n    defer s.wg.Done()\n    defer fmt.Printf(\"Worker %d stopped\\n\", id)\n\n    ticker := time.NewTicker(time.Second)\n    defer ticker.Stop()\n\n    for {\n        select {\n        case \u003C-ctx.Done():\n            \u002F\u002F Cleanup\n            fmt.Printf(\"Worker %d cleaning up...\\n\", id)\n            time.Sleep(500 * time.Millisecond) \u002F\u002F Simulated cleanup\n            return\n        case \u003C-ticker.C:\n            fmt.Printf(\"Worker %d working...\\n\", id)\n        }\n    }\n}\n\nfunc (s *Server) Shutdown(timeout time.Duration) {\n    \u002F\u002F Signal shutdown\n    close(s.shutdown)\n\n    \u002F\u002F Wait with timeout\n    done := make(chan struct{})\n    go func() {\n        s.wg.Wait()\n        close(done)\n    }()\n\n    select {\n    case \u003C-done:\n        fmt.Println(\"Clean shutdown completed\")\n    case \u003C-time.After(timeout):\n        fmt.Println(\"Shutdown timed out, forcing exit\")\n    }\n}\n\nfunc main() {\n    \u002F\u002F Setup signal handling\n    ctx, cancel := context.WithCancel(context.Background())\n\n    sigCh := make(chan os.Signal, 1)\n    signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)\n\n    server := NewServer()\n    server.Start(ctx)\n\n    \u002F\u002F Wait for signal\n    sig := \u003C-sigCh\n    fmt.Printf(\"\\nReceived signal: %v\\n\", sig)\n\n    \u002F\u002F Cancel context to stop workers\n    cancel()\n\n    \u002F\u002F Wait for graceful shutdown\n    server.Shutdown(5 * time.Second)\n}\n```\n\n### Pattern 5: Error Group with Cancellation\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"golang.org\u002Fx\u002Fsync\u002Ferrgroup\"\n    \"net\u002Fhttp\"\n)\n\nfunc fetchAllURLs(ctx context.Context, urls []string) ([]string, error) {\n    g, ctx := errgroup.WithContext(ctx)\n\n    results := make([]string, len(urls))\n\n    for i, url := range urls {\n        i, url := i, url \u002F\u002F Capture loop variables\n\n        g.Go(func() error {\n            req, err := http.NewRequestWithContext(ctx, \"GET\", url, nil)\n            if err != nil {\n                return fmt.Errorf(\"creating request for %s: %w\", url, err)\n            }\n\n            resp, err := http.DefaultClient.Do(req)\n            if err != nil {\n                return fmt.Errorf(\"fetching %s: %w\", url, err)\n            }\n            defer resp.Body.Close()\n\n            results[i] = fmt.Sprintf(\"%s: %d\", url, resp.StatusCode)\n            return nil\n        })\n    }\n\n    \u002F\u002F Wait for all goroutines to complete or one to fail\n    if err := g.Wait(); err != nil {\n        return nil, err \u002F\u002F First error cancels all others\n    }\n\n    return results, nil\n}\n\n\u002F\u002F With concurrency limit\nfunc fetchWithLimit(ctx context.Context, urls []string, limit int) ([]string, error) {\n    g, ctx := errgroup.WithContext(ctx)\n    g.SetLimit(limit) \u002F\u002F Max concurrent goroutines\n\n    results := make([]string, len(urls))\n    var mu sync.Mutex\n\n    for i, url := range urls {\n        i, url := i, url\n\n        g.Go(func() error {\n            result, err := fetchURL(ctx, url)\n            if err != nil {\n                return err\n            }\n\n            mu.Lock()\n            results[i] = result\n            mu.Unlock()\n            return nil\n        })\n    }\n\n    if err := g.Wait(); err != nil {\n        return nil, err\n    }\n\n    return results, nil\n}\n```\n\n### Pattern 6: Concurrent Map with sync.Map\n\n```go\npackage main\n\nimport (\n    \"sync\"\n)\n\n\u002F\u002F For frequent reads, infrequent writes\ntype Cache struct {\n    m sync.Map\n}\n\nfunc (c *Cache) Get(key string) (interface{}, bool) {\n    return c.m.Load(key)\n}\n\nfunc (c *Cache) Set(key string, value interface{}) {\n    c.m.Store(key, value)\n}\n\nfunc (c *Cache) GetOrSet(key string, value interface{}) (interface{}, bool) {\n    return c.m.LoadOrStore(key, value)\n}\n\nfunc (c *Cache) Delete(key string) {\n    c.m.Delete(key)\n}\n\n\u002F\u002F For write-heavy workloads, use sharded map\ntype ShardedMap struct {\n    shards    []*shard\n    numShards int\n}\n\ntype shard struct {\n    sync.RWMutex\n    data map[string]interface{}\n}\n\nfunc NewShardedMap(numShards int) *ShardedMap {\n    m := &ShardedMap{\n        shards:    make([]*shard, numShards),\n        numShards: numShards,\n    }\n    for i := range m.shards {\n        m.shards[i] = &shard{data: make(map[string]interface{})}\n    }\n    return m\n}\n\nfunc (m *ShardedMap) getShard(key string) *shard {\n    \u002F\u002F Simple hash\n    h := 0\n    for _, c := range key {\n        h = 31*h + int(c)\n    }\n    return m.shards[h%m.numShards]\n}\n\nfunc (m *ShardedMap) Get(key string) (interface{}, bool) {\n    shard := m.getShard(key)\n    shard.RLock()\n    defer shard.RUnlock()\n    v, ok := shard.data[key]\n    return v, ok\n}\n\nfunc (m *ShardedMap) Set(key string, value interface{}) {\n    shard := m.getShard(key)\n    shard.Lock()\n    defer shard.Unlock()\n    shard.data[key] = value\n}\n```\n\n### Pattern 7: Select with Timeout and Default\n\n```go\nfunc selectPatterns() {\n    ch := make(chan int)\n\n    \u002F\u002F Timeout pattern\n    select {\n    case v := \u003C-ch:\n        fmt.Println(\"Received:\", v)\n    case \u003C-time.After(time.Second):\n        fmt.Println(\"Timeout!\")\n    }\n\n    \u002F\u002F Non-blocking send\u002Freceive\n    select {\n    case ch \u003C- 42:\n        fmt.Println(\"Sent\")\n    default:\n        fmt.Println(\"Channel full, skipping\")\n    }\n\n    \u002F\u002F Priority select (check high priority first)\n    highPriority := make(chan int)\n    lowPriority := make(chan int)\n\n    for {\n        select {\n        case msg := \u003C-highPriority:\n            fmt.Println(\"High priority:\", msg)\n        default:\n            select {\n            case msg := \u003C-highPriority:\n                fmt.Println(\"High priority:\", msg)\n            case msg := \u003C-lowPriority:\n                fmt.Println(\"Low priority:\", msg)\n            }\n        }\n    }\n}\n```\n\n## Race Detection\n\n```bash\n# Run tests with race detector\ngo test -race .\u002F...\n\n# Build with race detector\ngo build -race .\n\n# Run with race detector\ngo run -race main.go\n```\n\n## Best Practices\n\n### Do's\n- **Use context** - For cancellation and deadlines\n- **Close channels** - From sender side only\n- **Use errgroup** - For concurrent operations with errors\n- **Buffer channels** - When you know the count\n- **Prefer channels** - Over mutexes when possible\n\n### Don'ts\n- **Don't leak goroutines** - Always have exit path\n- **Don't close from receiver** - Causes panic\n- **Don't use shared memory** - Unless necessary\n- **Don't ignore context cancellation** - Check ctx.Done()\n- **Don't use time.Sleep for sync** - Use proper primitives\n\n## Resources\n\n- [Go Concurrency Patterns](https:\u002F\u002Fgo.dev\u002Fblog\u002Fpipelines)\n- [Effective Go - Concurrency](https:\u002F\u002Fgo.dev\u002Fdoc\u002Feffective_go#concurrency)\n- [Go by Example - Goroutines](https:\u002F\u002Fgobyexample.com\u002Fgoroutines)\n","# Go Concurrency Patterns\n\nProduction patterns for Go concurrency including goroutines, channels, synchronization primitives, and context management.\n\n## When to Use This Skill\n\n- Building concurrent Go applications\n- Implementing worker pools and pipelines\n- Managing goroutine lifecycles\n- Using channels for communication\n- Debugging race conditions\n- Implementing graceful shutdown\n\n## Core Concepts\n\n### 1. Go Concurrency Primitives\n\n| Primitive | Purpose |\n|-----------|---------|\n| `goroutine` | Lightweight concurrent execution |\n| `channel` | Communication between goroutines |\n| `select` | Multiplex channel operations |\n| `sync.Mutex` | Mutual exclusion |\n| `sync.WaitGroup` | Wait for goroutines to complete |\n| `context.Context` | Cancellation and deadlines |\n\n### 2. Go Concurrency Mantra\n\n```\nDon't communicate by sharing memory;\nshare memory by communicating.\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"sync\"\n    \"time\"\n)\n\nfunc main() {\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n\n    results := make(chan string, 10)\n    var wg sync.WaitGroup\n\n    \u002F\u002F Spawn workers\n    for i := 0; i \u003C 3; i++ {\n        wg.Add(1)\n        go worker(ctx, i, results, &wg)\n    }\n\n    \u002F\u002F Close results when done\n    go func() {\n        wg.Wait()\n        close(results)\n    }()\n\n    \u002F\u002F Collect results\n    for result := range results {\n        fmt.Println(result)\n    }\n}\n\nfunc worker(ctx context.Context, id int, results chan\u003C- string, wg *sync.WaitGroup) {\n    defer wg.Done()\n\n    select {\n    case \u003C-ctx.Done():\n        return\n    case results \u003C- fmt.Sprintf(\"Worker %d done\", id):\n    }\n}\n```\n\n## Patterns\n\n### Pattern 1: Worker Pool\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"sync\"\n)\n\ntype Job struct {\n    ID   int\n    Data string\n}\n\ntype Result struct {\n    JobID int\n    Output string\n    Err   error\n}\n\nfunc WorkerPool(ctx context.Context, numWorkers int, jobs \u003C-chan Job) \u003C-chan Result {\n    results := make(chan Result, len(jobs))\n\n    var wg sync.WaitGroup\n    for i := 0; i \u003C numWorkers; i++ {\n        wg.Add(1)\n        go func(workerID int) {\n            defer wg.Done()\n            for job := range jobs {\n                select {\n                case \u003C-ctx.Done():\n                    return\n                default:\n                    result := processJob(job)\n                    results \u003C- result\n                }\n            }\n        }(i)\n    }\n\n    go func() {\n        wg.Wait()\n        close(results)\n    }()\n\n    return results\n}\n\nfunc processJob(job Job) Result {\n    \u002F\u002F Simulate work\n    return Result{\n        JobID:  job.ID,\n        Output: fmt.Sprintf(\"Processed: %s\", job.Data),\n    }\n}\n\n\u002F\u002F Usage\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    jobs := make(chan Job, 100)\n\n    \u002F\u002F Send jobs\n    go func() {\n        for i := 0; i \u003C 50; i++ {\n            jobs \u003C- Job{ID: i, Data: fmt.Sprintf(\"job-%d\", i)}\n        }\n        close(jobs)\n    }()\n\n    \u002F\u002F Process with 5 workers\n    results := WorkerPool(ctx, 5, jobs)\n\n    for result := range results {\n        fmt.Printf(\"Result: %+v\\n\", result)\n    }\n}\n```\n\n### Pattern 2: Fan-Out\u002FFan-In Pipeline\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"sync\"\n)\n\n\u002F\u002F Stage 1: Generate numbers\nfunc generate(ctx context.Context, nums ...int) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for _, n := range nums {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n:\n            }\n        }\n    }()\n    return out\n}\n\n\u002F\u002F Stage 2: Square numbers (can run multiple instances)\nfunc square(ctx context.Context, in \u003C-chan int) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for n := range in {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n * n:\n            }\n        }\n    }()\n    return out\n}\n\n\u002F\u002F Fan-in: Merge multiple channels into one\nfunc merge(ctx context.Context, cs ...\u003C-chan int) \u003C-chan int {\n    var wg sync.WaitGroup\n    out := make(chan int)\n\n    \u002F\u002F Start output goroutine for each input channel\n    output := func(c \u003C-chan int) {\n        defer wg.Done()\n        for n := range c {\n            select {\n            case \u003C-ctx.Done():\n                return\n            case out \u003C- n:\n            }\n        }\n    }\n\n    wg.Add(len(cs))\n    for _, c := range cs {\n        go output(c)\n    }\n\n    \u002F\u002F Close out after all inputs are done\n    go func() {\n        wg.Wait()\n        close(out)\n    }()\n\n    return out\n}\n\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    \u002F\u002F Generate input\n    in := generate(ctx, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)\n\n    \u002F\u002F Fan out to multiple squarers\n    c1 := square(ctx, in)\n    c2 := square(ctx, in)\n    c3 := square(ctx, in)\n\n    \u002F\u002F Fan in results\n    for result := range merge(ctx, c1, c2, c3) {\n        fmt.Println(result)\n    }\n}\n```\n\n### Pattern 3: Bounded Concurrency with Semaphore\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"golang.org\u002Fx\u002Fsync\u002Fsemaphore\"\n    \"sync\"\n)\n\ntype RateLimitedWorker struct {\n    sem *semaphore.Weighted\n}\n\nfunc NewRateLimitedWorker(maxConcurrent int64) *RateLimitedWorker {\n    return &RateLimitedWorker{\n        sem: semaphore.NewWeighted(maxConcurrent),\n    }\n}\n\nfunc (w *RateLimitedWorker) Do(ctx context.Context, tasks []func() error) []error {\n    var (\n        wg     sync.WaitGroup\n        mu     sync.Mutex\n        errors []error\n    )\n\n    for _, task := range tasks {\n        \u002F\u002F Acquire semaphore (blocks if at limit)\n        if err := w.sem.Acquire(ctx, 1); err != nil {\n            return []error{err}\n        }\n\n        wg.Add(1)\n        go func(t func() error) {\n            defer wg.Done()\n            defer w.sem.Release(1)\n\n            if err := t(); err != nil {\n                mu.Lock()\n                errors = append(errors, err)\n                mu.Unlock()\n            }\n        }(task)\n    }\n\n    wg.Wait()\n    return errors\n}\n\n\u002F\u002F Alternative: Channel-based semaphore\ntype Semaphore chan struct{}\n\nfunc NewSemaphore(n int) Semaphore {\n    return make(chan struct{}, n)\n}\n\nfunc (s Semaphore) Acquire() {\n    s \u003C- struct{}{}\n}\n\nfunc (s Semaphore) Release() {\n    \u003C-s\n}\n```\n\n### Pattern 4: Graceful Shutdown\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"os\"\n    \"os\u002Fsignal\"\n    \"sync\"\n    \"syscall\"\n    \"time\"\n)\n\ntype Server struct {\n    shutdown chan struct{}\n    wg       sync.WaitGroup\n}\n\nfunc NewServer() *Server {\n    return &Server{\n        shutdown: make(chan struct{}),\n    }\n}\n\nfunc (s *Server) Start(ctx context.Context) {\n    \u002F\u002F Start workers\n    for i := 0; i \u003C 5; i++ {\n        s.wg.Add(1)\n        go s.worker(ctx, i)\n    }\n}\n\nfunc (s *Server) worker(ctx context.Context, id int) {\n    defer s.wg.Done()\n    defer fmt.Printf(\"Worker %d stopped\\n\", id)\n\n    ticker := time.NewTicker(time.Second)\n    defer ticker.Stop()\n\n    for {\n        select {\n        case \u003C-ctx.Done():\n            \u002F\u002F Cleanup\n            fmt.Printf(\"Worker %d cleaning up...\\n\", id)\n            time.Sleep(500 * time.Millisecond) \u002F\u002F Simulated cleanup\n            return\n        case \u003C-ticker.C:\n            fmt.Printf(\"Worker %d working...\\n\", id)\n        }\n    }\n}\n\nfunc (s *Server) Shutdown(timeout time.Duration) {\n    \u002F\u002F Signal shutdown\n    close(s.shutdown)\n\n    \u002F\u002F Wait with timeout\n    done := make(chan struct{})\n    go func() {\n        s.wg.Wait()\n        close(done)\n    }()\n\n    select {\n    case \u003C-done:\n        fmt.Println(\"Clean shutdown completed\")\n    case \u003C-time.After(timeout):\n        fmt.Println(\"Shutdown timed out, forcing exit\")\n    }\n}\n\nfunc main() {\n    \u002F\u002F Setup signal handling\n    ctx, cancel := context.WithCancel(context.Background())\n\n    sigCh := make(chan os.Signal, 1)\n    signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)\n\n    server := NewServer()\n    server.Start(ctx)\n\n    \u002F\u002F Wait for signal\n    sig := \u003C-sigCh\n    fmt.Printf(\"\\nReceived signal: %v\\n\", sig)\n\n    \u002F\u002F Cancel context to stop workers\n    cancel()\n\n    \u002F\u002F Wait for graceful shutdown\n    server.Shutdown(5 * time.Second)\n}\n```\n\n### Pattern 5: Error Group with Cancellation\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"golang.org\u002Fx\u002Fsync\u002Ferrgroup\"\n    \"net\u002Fhttp\"\n)\n\nfunc fetchAllURLs(ctx context.Context, urls []string) ([]string, error) {\n    g, ctx := errgroup.WithContext(ctx)\n\n    results := make([]string, len(urls))\n\n    for i, url := range urls {\n        i, url := i, url \u002F\u002F Capture loop variables\n\n        g.Go(func() error {\n            req, err := http.NewRequestWithContext(ctx, \"GET\", url, nil)\n            if err != nil {\n                return fmt.Errorf(\"creating request for %s: %w\", url, err)\n            }\n\n            resp, err := http.DefaultClient.Do(req)\n            if err != nil {\n                return fmt.Errorf(\"fetching %s: %w\", url, err)\n            }\n            defer resp.Body.Close()\n\n            results[i] = fmt.Sprintf(\"%s: %d\", url, resp.StatusCode)\n            return nil\n        })\n    }\n\n    \u002F\u002F Wait for all goroutines to complete or one to fail\n    if err := g.Wait(); err != nil {\n        return nil, err \u002F\u002F First error cancels all others\n    }\n\n    return results, nil\n}\n\n\u002F\u002F With concurrency limit\nfunc fetchWithLimit(ctx context.Context, urls []string, limit int) ([]string, error) {\n    g, ctx := errgroup.WithContext(ctx)\n    g.SetLimit(limit) \u002F\u002F Max concurrent goroutines\n\n    results := make([]string, len(urls))\n    var mu sync.Mutex\n\n    for i, url := range urls {\n        i, url := i, url\n\n        g.Go(func() error {\n            result, err := fetchURL(ctx, url)\n            if err != nil {\n                return err\n            }\n\n            mu.Lock()\n            results[i] = result\n            mu.Unlock()\n            return nil\n        })\n    }\n\n    if err := g.Wait(); err != nil {\n        return nil, err\n    }\n\n    return results, nil\n}\n```\n\n### Pattern 6: Concurrent Map with sync.Map\n\n```go\npackage main\n\nimport (\n    \"sync\"\n)\n\n\u002F\u002F For frequent reads, infrequent writes\ntype Cache struct {\n    m sync.Map\n}\n\nfunc (c *Cache) Get(key string) (interface{}, bool) {\n    return c.m.Load(key)\n}\n\nfunc (c *Cache) Set(key string, value interface{}) {\n    c.m.Store(key, value)\n}\n\nfunc (c *Cache) GetOrSet(key string, value interface{}) (interface{}, bool) {\n    return c.m.LoadOrStore(key, value)\n}\n\nfunc (c *Cache) Delete(key string) {\n    c.m.Delete(key)\n}\n\n\u002F\u002F For write-heavy workloads, use sharded map\ntype ShardedMap struct {\n    shards    []*shard\n    numShards int\n}\n\ntype shard struct {\n    sync.RWMutex\n    data map[string]interface{}\n}\n\nfunc NewShardedMap(numShards int) *ShardedMap {\n    m := &ShardedMap{\n        shards:    make([]*shard, numShards),\n        numShards: numShards,\n    }\n    for i := range m.shards {\n        m.shards[i] = &shard{data: make(map[string]interface{})}\n    }\n    return m\n}\n\nfunc (m *ShardedMap) getShard(key string) *shard {\n    \u002F\u002F Simple hash\n    h := 0\n    for _, c := range key {\n        h = 31*h + int(c)\n    }\n    return m.shards[h%m.numShards]\n}\n\nfunc (m *ShardedMap) Get(key string) (interface{}, bool) {\n    shard := m.getShard(key)\n    shard.RLock()\n    defer shard.RUnlock()\n    v, ok := shard.data[key]\n    return v, ok\n}\n\nfunc (m *ShardedMap) Set(key string, value interface{}) {\n    shard := m.getShard(key)\n    shard.Lock()\n    defer shard.Unlock()\n    shard.data[key] = value\n}\n```\n\n### Pattern 7: Select with Timeout and Default\n\n```go\nfunc selectPatterns() {\n    ch := make(chan int)\n\n    \u002F\u002F Timeout pattern\n    select {\n    case v := \u003C-ch:\n        fmt.Println(\"Received:\", v)\n    case \u003C-time.After(time.Second):\n        fmt.Println(\"Timeout!\")\n    }\n\n    \u002F\u002F Non-blocking send\u002Freceive\n    select {\n    case ch \u003C- 42:\n        fmt.Println(\"Sent\")\n    default:\n        fmt.Println(\"Channel full, skipping\")\n    }\n\n    \u002F\u002F Priority select (check high priority first)\n    highPriority := make(chan int)\n    lowPriority := make(chan int)\n\n    for {\n        select {\n        case msg := \u003C-highPriority:\n            fmt.Println(\"High priority:\", msg)\n        default:\n            select {\n            case msg := \u003C-highPriority:\n                fmt.Println(\"High priority:\", msg)\n            case msg := \u003C-lowPriority:\n                fmt.Println(\"Low priority:\", msg)\n            }\n        }\n    }\n}\n```\n\n## Race Detection\n\n```bash\n# Run tests with race detector\ngo test -race .\u002F...\n\n# Build with race detector\ngo build -race .\n\n# Run with race detector\ngo run -race main.go\n```\n\n## Best Practices\n\n### Do's\n- **Use context** - For cancellation and deadlines\n- **Close channels** - From sender side only\n- **Use errgroup** - For concurrent operations with errors\n- **Buffer channels** - When you know the count\n- **Prefer channels** - Over mutexes when possible\n\n### Don'ts\n- **Don't leak goroutines** - Always have exit path\n- **Don't close from receiver** - Causes panic\n- **Don't use shared memory** - Unless necessary\n- **Don't ignore context cancellation** - Check ctx.Done()\n- **Don't use time.Sleep for sync** - Use proper primitives\n\n## Resources\n\n- [Go Concurrency Patterns](https:\u002F\u002Fgo.dev\u002Fblog\u002Fpipelines)\n- [Effective Go - Concurrency](https:\u002F\u002Fgo.dev\u002Fdoc\u002Feffective_go#concurrency)\n- [Go by Example - Goroutines](https:\u002F\u002Fgobyexample.com\u002Fgoroutines)\n",{"name":5,"description":7},"main","def8531e65114c0fca8fb8551c1871ee0eed705c","skills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fgo-concurrency-patterns",24,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub","https:\u002F\u002Fskillshub.wtf","https:\u002F\u002Fgithub.com\u002FComeOnOliver","AI & Blockchain Engineer\r\n","2026-06-24 22:07:19",{"source":8,"skillName":5,"commands":49,"prompt":59,"zipAvailable":60},[50,53,56],{"tool":51,"command":52},"npx","npx skills add ComeOnOliver\u002Fskillshub --skill go-concurrency-patterns",{"tool":54,"command":55},"bunx","bunx skills add ComeOnOliver\u002Fskillshub --skill go-concurrency-patterns",{"tool":57,"command":58},"pnpm","pnpm dlx skills add ComeOnOliver\u002Fskillshub --skill go-concurrency-patterns","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fgo-concurrency-patterns\u002FSKILL.md",true,[62],{"path":63,"resourceType":64,"resourceTypeDesc":65,"sizeBytes":66,"sourceHtmlUrl":67},"skill-report.json","REFERENCE","参考文档",8363,"https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Faiskillstore\u002Fmarketplace\u002Fwshobson\u002Fgo-concurrency-patterns\u002Fskill-report.json",[69,83,93,102,108,115],{"skillNo":70,"name":71,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":72,"categories":73,"keywords":79,"publishedAt":34},"01M6FKJN13GMG4KY57J3QD8GY5","model-hierarchy-skill","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzscole\u002Fmodel-hierarchy-skill\u002Fmodel-hierarchy-skill\u002FSKILL.md",[74],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[80,81,82],"model","model hierarchy","model hierarchy skill",{"skillNo":84,"name":85,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":86,"categories":87,"keywords":89,"publishedAt":34},"01M6FKJJJ6GCAMR4PPPRD12M5X","zoom-mcp","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-mcp\u002FSKILL.md",[88],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},[90,91,92],"MCP","zoom","zoom mcp",{"skillNo":94,"name":95,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":96,"categories":97,"keywords":98,"publishedAt":34},"01M6FKJE3KYT3KPEZMEEHYKA6K","zoom-apps-sdk","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fzoom-apps-sdk\u002FSKILL.md",[],[99,100,101,91],"sdk","zoom apps sdk","apps",{"skillNo":103,"name":104,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":105,"categories":106,"keywords":107,"publishedAt":34},"01M6FKJ9EDH9Q5VWDF8HAJRSP0","windows","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwindows\u002FSKILL.md",[],[104],{"skillNo":109,"name":110,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":111,"categories":112,"keywords":114,"publishedAt":34},"01M6FKJ666BEEFC3AE9GJ72Q93","whiteboard","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwhiteboard\u002FSKILL.md",[113],{"categoryNo":75,"categoryName":76,"taxonomyType":77,"taxonomyTypeDesc":78},[110],{"skillNo":116,"name":117,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":118,"categories":119,"keywords":120,"publishedAt":34},"01M6FKJ21WFCDGTJQ2Q91T9DVW","websockets","https:\u002F\u002Fgithub.com\u002FComeOnOliver\u002Fskillshub\u002Fblob\u002Fmain\u002Fskills\u002Fzoom\u002Fskills\u002Fwebsockets\u002FSKILL.md",[],[117]]