[{"data":1,"prerenderedAt":3527},["ShallowReactive",2],{"page-\u002Fgo\u002F26-concurrency-patterns":3},{"id":4,"title":5,"body":6,"description":3520,"extension":3521,"meta":3522,"navigation":51,"path":3523,"seo":3524,"stem":3525,"__hash__":3526},"content\u002Fgo\u002F26-concurrency-patterns.md","26 — Concurrency Patterns",{"type":7,"value":8,"toc":3515},"minimark",[9,13,18,3511],[10,11,5],"h1",{"id":12},"_26-concurrency-patterns",[14,15,17],"h2",{"id":16},"worker-pool-bounded-concurrency","Worker Pool — Bounded Concurrency",[19,20,22,769,773,3431,3498,3502],"code-wrapper",{"language":21},"go",[23,24,28],"pre",{"className":25,"code":26,"language":21,"meta":27,"style":27},"language-go shiki shiki-themes github-light github-dark","\u002F\u002F Worker pool: N goroutines process jobs from a channel.\n\u002F\u002F Bounded concurrency — prevents overwhelming downstream resources.\n\ntype Job struct {\n    ID    int\n    Input any\n}\ntype Result struct {\n    JobID  int\n    Output any\n    Err    error\n}\n\nfunc worker(ctx context.Context, id int, jobs \u003C-chan Job, results chan\u003C- Result,\n    wg *sync.WaitGroup) {\n    defer wg.Done()\n    for {\n        select {\n        case job, ok := \u003C-jobs:\n            if !ok {\n                return  \u002F\u002F jobs channel closed — clean exit\n            }\n            \u002F\u002F Process with cancellation:\n            output, err := process(ctx, job.Input)\n            select {\n            case results \u003C- Result{JobID: job.ID, Output: output, Err: err}:\n            case \u003C-ctx.Done():\n                return  \u002F\u002F cancelled while sending result\n            }\n        case \u003C-ctx.Done():\n            return  \u002F\u002F cancelled\n        }\n    }\n}\n\nfunc runWorkerPool(ctx context.Context, jobs []Job, numWorkers int) []Result {\n    jobCh := make(chan Job, len(jobs))\n    resultCh := make(chan Result, len(jobs))\n    var wg sync.WaitGroup\n\n    \u002F\u002F Start workers:\n    for i := 0; i \u003C numWorkers; i++ {\n        wg.Add(1)\n        go worker(ctx, i, jobCh, resultCh, &wg)\n    }\n\n    \u002F\u002F Send jobs (buffered — doesn't block):\n    for _, job := range jobs {\n        jobCh \u003C- job\n    }\n    close(jobCh)  \u002F\u002F signal workers that no more jobs are coming\n\n    \u002F\u002F Close results after all workers finish:\n    go func() {\n        wg.Wait()\n        close(resultCh)\n    }()\n\n    \u002F\u002F Collect results:\n    var results []Result\n    for r := range resultCh {\n        results = append(results, r)\n    }\n    return results\n}\n","",[29,30,31,40,46,53,71,80,89,95,107,115,123,132,137,142,199,219,234,242,250,268,280,289,295,301,315,323,338,353,361,366,379,388,394,400,405,410,453,479,501,517,522,528,556,573,590,595,600,606,622,633,638,650,655,661,673,683,692,698,703,709,720,735,750,755,764],"code",{"__ignoreMap":27},[32,33,36],"span",{"class":34,"line":35},"line",1,[32,37,39],{"class":38},"sdCPZ","\u002F\u002F Worker pool: N goroutines process jobs from a channel.\n",[32,41,43],{"class":34,"line":42},2,[32,44,45],{"class":38},"\u002F\u002F Bounded concurrency — prevents overwhelming downstream resources.\n",[32,47,49],{"class":34,"line":48},3,[32,50,52],{"emptyLinePlaceholder":51},true,"\n",[32,54,56,60,64,67],{"class":34,"line":55},4,[32,57,59],{"class":58},"svdQ7","type",[32,61,63],{"class":62},"sIsaT"," Job",[32,65,66],{"class":58}," struct",[32,68,70],{"class":69},"ssxIu"," {\n",[32,72,74,77],{"class":34,"line":73},5,[32,75,76],{"class":69},"    ID    ",[32,78,79],{"class":58},"int\n",[32,81,83,86],{"class":34,"line":82},6,[32,84,85],{"class":69},"    Input ",[32,87,88],{"class":62},"any\n",[32,90,92],{"class":34,"line":91},7,[32,93,94],{"class":69},"}\n",[32,96,98,100,103,105],{"class":34,"line":97},8,[32,99,59],{"class":58},[32,101,102],{"class":62}," Result",[32,104,66],{"class":58},[32,106,70],{"class":69},[32,108,110,113],{"class":34,"line":109},9,[32,111,112],{"class":69},"    JobID  ",[32,114,79],{"class":58},[32,116,118,121],{"class":34,"line":117},10,[32,119,120],{"class":69},"    Output ",[32,122,88],{"class":62},[32,124,126,129],{"class":34,"line":125},11,[32,127,128],{"class":69},"    Err    ",[32,130,131],{"class":58},"error\n",[32,133,135],{"class":34,"line":134},12,[32,136,94],{"class":69},[32,138,140],{"class":34,"line":139},13,[32,141,52],{"emptyLinePlaceholder":51},[32,143,145,148,151,154,158,161,164,167,170,173,176,178,181,184,186,188,191,194,196],{"class":34,"line":144},14,[32,146,147],{"class":58},"func",[32,149,150],{"class":62}," worker",[32,152,153],{"class":69},"(",[32,155,157],{"class":156},"sCrzJ","ctx",[32,159,160],{"class":62}," context",[32,162,163],{"class":69},".",[32,165,166],{"class":62},"Context",[32,168,169],{"class":69},", ",[32,171,172],{"class":156},"id",[32,174,175],{"class":58}," int",[32,177,169],{"class":69},[32,179,180],{"class":156},"jobs",[32,182,183],{"class":58}," \u003C-chan",[32,185,63],{"class":62},[32,187,169],{"class":69},[32,189,190],{"class":156},"results",[32,192,193],{"class":58}," chan\u003C-",[32,195,102],{"class":62},[32,197,198],{"class":69},",\n",[32,200,202,205,208,211,213,216],{"class":34,"line":201},15,[32,203,204],{"class":156},"    wg",[32,206,207],{"class":58}," *",[32,209,210],{"class":62},"sync",[32,212,163],{"class":69},[32,214,215],{"class":62},"WaitGroup",[32,217,218],{"class":69},") {\n",[32,220,222,225,228,231],{"class":34,"line":221},16,[32,223,224],{"class":58},"    defer",[32,226,227],{"class":69}," wg.",[32,229,230],{"class":62},"Done",[32,232,233],{"class":69},"()\n",[32,235,237,240],{"class":34,"line":236},17,[32,238,239],{"class":58},"    for",[32,241,70],{"class":69},[32,243,245,248],{"class":34,"line":244},18,[32,246,247],{"class":58},"        select",[32,249,70],{"class":69},[32,251,253,256,259,262,265],{"class":34,"line":252},19,[32,254,255],{"class":58},"        case",[32,257,258],{"class":69}," job, ok ",[32,260,261],{"class":58},":=",[32,263,264],{"class":58}," \u003C-",[32,266,267],{"class":69},"jobs:\n",[32,269,271,274,277],{"class":34,"line":270},20,[32,272,273],{"class":58},"            if",[32,275,276],{"class":58}," !",[32,278,279],{"class":69},"ok {\n",[32,281,283,286],{"class":34,"line":282},21,[32,284,285],{"class":58},"                return",[32,287,288],{"class":38},"  \u002F\u002F jobs channel closed — clean exit\n",[32,290,292],{"class":34,"line":291},22,[32,293,294],{"class":69},"            }\n",[32,296,298],{"class":34,"line":297},23,[32,299,300],{"class":38},"            \u002F\u002F Process with cancellation:\n",[32,302,304,307,309,312],{"class":34,"line":303},24,[32,305,306],{"class":69},"            output, err ",[32,308,261],{"class":58},[32,310,311],{"class":62}," process",[32,313,314],{"class":69},"(ctx, job.Input)\n",[32,316,318,321],{"class":34,"line":317},25,[32,319,320],{"class":58},"            select",[32,322,70],{"class":69},[32,324,326,329,332,335],{"class":34,"line":325},26,[32,327,328],{"class":58},"            case",[32,330,331],{"class":69}," results ",[32,333,334],{"class":58},"\u003C-",[32,336,337],{"class":69}," Result{JobID: job.ID, Output: output, Err: err}:\n",[32,339,341,343,345,348,350],{"class":34,"line":340},27,[32,342,328],{"class":58},[32,344,264],{"class":58},[32,346,347],{"class":69},"ctx.",[32,349,230],{"class":62},[32,351,352],{"class":69},"():\n",[32,354,356,358],{"class":34,"line":355},28,[32,357,285],{"class":58},[32,359,360],{"class":38},"  \u002F\u002F cancelled while sending result\n",[32,362,364],{"class":34,"line":363},29,[32,365,294],{"class":69},[32,367,369,371,373,375,377],{"class":34,"line":368},30,[32,370,255],{"class":58},[32,372,264],{"class":58},[32,374,347],{"class":69},[32,376,230],{"class":62},[32,378,352],{"class":69},[32,380,382,385],{"class":34,"line":381},31,[32,383,384],{"class":58},"            return",[32,386,387],{"class":38},"  \u002F\u002F cancelled\n",[32,389,391],{"class":34,"line":390},32,[32,392,393],{"class":69},"        }\n",[32,395,397],{"class":34,"line":396},33,[32,398,399],{"class":69},"    }\n",[32,401,403],{"class":34,"line":402},34,[32,404,94],{"class":69},[32,406,408],{"class":34,"line":407},35,[32,409,52],{"emptyLinePlaceholder":51},[32,411,413,415,418,420,422,424,426,428,430,432,435,438,440,443,445,448,451],{"class":34,"line":412},36,[32,414,147],{"class":58},[32,416,417],{"class":62}," runWorkerPool",[32,419,153],{"class":69},[32,421,157],{"class":156},[32,423,160],{"class":62},[32,425,163],{"class":69},[32,427,166],{"class":62},[32,429,169],{"class":69},[32,431,180],{"class":156},[32,433,434],{"class":69}," []",[32,436,437],{"class":62},"Job",[32,439,169],{"class":69},[32,441,442],{"class":156},"numWorkers",[32,444,175],{"class":58},[32,446,447],{"class":69},") []",[32,449,450],{"class":62},"Result",[32,452,70],{"class":69},[32,454,456,459,461,464,466,469,471,473,476],{"class":34,"line":455},37,[32,457,458],{"class":69},"    jobCh ",[32,460,261],{"class":58},[32,462,463],{"class":62}," make",[32,465,153],{"class":69},[32,467,468],{"class":58},"chan",[32,470,63],{"class":62},[32,472,169],{"class":69},[32,474,475],{"class":62},"len",[32,477,478],{"class":69},"(jobs))\n",[32,480,482,485,487,489,491,493,495,497,499],{"class":34,"line":481},38,[32,483,484],{"class":69},"    resultCh ",[32,486,261],{"class":58},[32,488,463],{"class":62},[32,490,153],{"class":69},[32,492,468],{"class":58},[32,494,102],{"class":62},[32,496,169],{"class":69},[32,498,475],{"class":62},[32,500,478],{"class":69},[32,502,504,507,510,512,514],{"class":34,"line":503},39,[32,505,506],{"class":58},"    var",[32,508,509],{"class":69}," wg ",[32,511,210],{"class":62},[32,513,163],{"class":69},[32,515,516],{"class":62},"WaitGroup\n",[32,518,520],{"class":34,"line":519},40,[32,521,52],{"emptyLinePlaceholder":51},[32,523,525],{"class":34,"line":524},41,[32,526,527],{"class":38},"    \u002F\u002F Start workers:\n",[32,529,531,533,536,538,542,545,548,551,554],{"class":34,"line":530},42,[32,532,239],{"class":58},[32,534,535],{"class":69}," i ",[32,537,261],{"class":58},[32,539,541],{"class":540},"snvgF"," 0",[32,543,544],{"class":69},"; i ",[32,546,547],{"class":58},"\u003C",[32,549,550],{"class":69}," numWorkers; i",[32,552,553],{"class":58},"++",[32,555,70],{"class":69},[32,557,559,562,565,567,570],{"class":34,"line":558},43,[32,560,561],{"class":69},"        wg.",[32,563,564],{"class":62},"Add",[32,566,153],{"class":69},[32,568,569],{"class":540},"1",[32,571,572],{"class":69},")\n",[32,574,576,579,581,584,587],{"class":34,"line":575},44,[32,577,578],{"class":58},"        go",[32,580,150],{"class":62},[32,582,583],{"class":69},"(ctx, i, jobCh, resultCh, ",[32,585,586],{"class":58},"&",[32,588,589],{"class":69},"wg)\n",[32,591,593],{"class":34,"line":592},45,[32,594,399],{"class":69},[32,596,598],{"class":34,"line":597},46,[32,599,52],{"emptyLinePlaceholder":51},[32,601,603],{"class":34,"line":602},47,[32,604,605],{"class":38},"    \u002F\u002F Send jobs (buffered — doesn't block):\n",[32,607,609,611,614,616,619],{"class":34,"line":608},48,[32,610,239],{"class":58},[32,612,613],{"class":69}," _, job ",[32,615,261],{"class":58},[32,617,618],{"class":58}," range",[32,620,621],{"class":69}," jobs {\n",[32,623,625,628,630],{"class":34,"line":624},49,[32,626,627],{"class":69},"        jobCh ",[32,629,334],{"class":58},[32,631,632],{"class":69}," job\n",[32,634,636],{"class":34,"line":635},50,[32,637,399],{"class":69},[32,639,641,644,647],{"class":34,"line":640},51,[32,642,643],{"class":62},"    close",[32,645,646],{"class":69},"(jobCh)  ",[32,648,649],{"class":38},"\u002F\u002F signal workers that no more jobs are coming\n",[32,651,653],{"class":34,"line":652},52,[32,654,52],{"emptyLinePlaceholder":51},[32,656,658],{"class":34,"line":657},53,[32,659,660],{"class":38},"    \u002F\u002F Close results after all workers finish:\n",[32,662,664,667,670],{"class":34,"line":663},54,[32,665,666],{"class":58},"    go",[32,668,669],{"class":58}," func",[32,671,672],{"class":69},"() {\n",[32,674,676,678,681],{"class":34,"line":675},55,[32,677,561],{"class":69},[32,679,680],{"class":62},"Wait",[32,682,233],{"class":69},[32,684,686,689],{"class":34,"line":685},56,[32,687,688],{"class":62},"        close",[32,690,691],{"class":69},"(resultCh)\n",[32,693,695],{"class":34,"line":694},57,[32,696,697],{"class":69},"    }()\n",[32,699,701],{"class":34,"line":700},58,[32,702,52],{"emptyLinePlaceholder":51},[32,704,706],{"class":34,"line":705},59,[32,707,708],{"class":38},"    \u002F\u002F Collect results:\n",[32,710,712,714,717],{"class":34,"line":711},60,[32,713,506],{"class":58},[32,715,716],{"class":69}," results []",[32,718,719],{"class":62},"Result\n",[32,721,723,725,728,730,732],{"class":34,"line":722},61,[32,724,239],{"class":58},[32,726,727],{"class":69}," r ",[32,729,261],{"class":58},[32,731,618],{"class":58},[32,733,734],{"class":69}," resultCh {\n",[32,736,738,741,744,747],{"class":34,"line":737},62,[32,739,740],{"class":69},"        results ",[32,742,743],{"class":58},"=",[32,745,746],{"class":62}," append",[32,748,749],{"class":69},"(results, r)\n",[32,751,753],{"class":34,"line":752},63,[32,754,399],{"class":69},[32,756,758,761],{"class":34,"line":757},64,[32,759,760],{"class":58},"    return",[32,762,763],{"class":69}," results\n",[32,765,767],{"class":34,"line":766},65,[32,768,94],{"class":69},[14,770,772],{"id":771},"pipeline-stage-by-stage-processing","Pipeline — Stage-by-Stage Processing",[19,774,775,1438,1442],{"language":21},[23,776,778],{"className":25,"code":777,"language":21,"meta":27,"style":27},"\u002F\u002F Pipeline: stages connected by channels, each a goroutine.\n\u002F\u002F Each stage reads from input, transforms, writes to output.\n\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 out \u003C- n:\n            case \u003C-ctx.Done():\n                return\n            }\n        }\n    }()\n    return out\n}\n\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 out \u003C- n * n:\n            case \u003C-ctx.Done():\n                return\n            }\n        }\n    }()\n    return out\n}\n\nfunc filter(ctx context.Context, in \u003C-chan int, pred func(int) bool) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for n := range in {\n            if pred(n) {\n                select {\n                case out \u003C- n:\n                case \u003C-ctx.Done():\n                    return\n                }\n            }\n        }\n    }()\n    return out\n}\n\nfunc main() {\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    \u002F\u002F Compose: generate → square → filter evens → print\n    nums := generate(ctx, 1, 2, 3, 4, 5)\n    squared := square(ctx, nums)\n    evens := filter(ctx, squared, func(n int) bool { return n%2 == 0 })\n\n    for n := range evens {\n        fmt.Println(n)  \u002F\u002F 4, 16 (2²=4, 4²=16)\n    }\n}\n",[29,779,780,785,790,794,829,846,854,865,880,886,898,910,915,919,923,927,934,938,942,976,992,1000,1008,1022,1028,1043,1055,1059,1063,1067,1071,1077,1081,1085,1135,1151,1159,1167,1179,1189,1196,1207,1219,1224,1229,1233,1237,1241,1247,1251,1255,1264,1286,1295,1299,1304,1340,1352,1399,1403,1416,1430,1434],{"__ignoreMap":27},[32,781,782],{"class":34,"line":35},[32,783,784],{"class":38},"\u002F\u002F Pipeline: stages connected by channels, each a goroutine.\n",[32,786,787],{"class":34,"line":42},[32,788,789],{"class":38},"\u002F\u002F Each stage reads from input, transforms, writes to output.\n",[32,791,792],{"class":34,"line":48},[32,793,52],{"emptyLinePlaceholder":51},[32,795,796,798,801,803,805,807,809,811,813,816,819,822,825,827],{"class":34,"line":55},[32,797,147],{"class":58},[32,799,800],{"class":62}," generate",[32,802,153],{"class":69},[32,804,157],{"class":156},[32,806,160],{"class":62},[32,808,163],{"class":69},[32,810,166],{"class":62},[32,812,169],{"class":69},[32,814,815],{"class":156},"nums",[32,817,818],{"class":58}," ...int",[32,820,821],{"class":69},") ",[32,823,824],{"class":58},"\u003C-chan",[32,826,175],{"class":58},[32,828,70],{"class":69},[32,830,831,834,836,838,840,842,844],{"class":34,"line":73},[32,832,833],{"class":69},"    out ",[32,835,261],{"class":58},[32,837,463],{"class":62},[32,839,153],{"class":69},[32,841,468],{"class":58},[32,843,175],{"class":58},[32,845,572],{"class":69},[32,847,848,850,852],{"class":34,"line":82},[32,849,666],{"class":58},[32,851,669],{"class":58},[32,853,672],{"class":69},[32,855,856,859,862],{"class":34,"line":91},[32,857,858],{"class":58},"        defer",[32,860,861],{"class":62}," close",[32,863,864],{"class":69},"(out)\n",[32,866,867,870,873,875,877],{"class":34,"line":97},[32,868,869],{"class":58},"        for",[32,871,872],{"class":69}," _, n ",[32,874,261],{"class":58},[32,876,618],{"class":58},[32,878,879],{"class":69}," nums {\n",[32,881,882,884],{"class":34,"line":109},[32,883,320],{"class":58},[32,885,70],{"class":69},[32,887,888,890,893,895],{"class":34,"line":117},[32,889,328],{"class":58},[32,891,892],{"class":69}," out ",[32,894,334],{"class":58},[32,896,897],{"class":69}," n:\n",[32,899,900,902,904,906,908],{"class":34,"line":125},[32,901,328],{"class":58},[32,903,264],{"class":58},[32,905,347],{"class":69},[32,907,230],{"class":62},[32,909,352],{"class":69},[32,911,912],{"class":34,"line":134},[32,913,914],{"class":58},"                return\n",[32,916,917],{"class":34,"line":139},[32,918,294],{"class":69},[32,920,921],{"class":34,"line":144},[32,922,393],{"class":69},[32,924,925],{"class":34,"line":201},[32,926,697],{"class":69},[32,928,929,931],{"class":34,"line":221},[32,930,760],{"class":58},[32,932,933],{"class":69}," out\n",[32,935,936],{"class":34,"line":236},[32,937,94],{"class":69},[32,939,940],{"class":34,"line":244},[32,941,52],{"emptyLinePlaceholder":51},[32,943,944,946,949,951,953,955,957,959,961,964,966,968,970,972,974],{"class":34,"line":252},[32,945,147],{"class":58},[32,947,948],{"class":62}," square",[32,950,153],{"class":69},[32,952,157],{"class":156},[32,954,160],{"class":62},[32,956,163],{"class":69},[32,958,166],{"class":62},[32,960,169],{"class":69},[32,962,963],{"class":156},"in",[32,965,183],{"class":58},[32,967,175],{"class":58},[32,969,821],{"class":69},[32,971,824],{"class":58},[32,973,175],{"class":58},[32,975,70],{"class":69},[32,977,978,980,982,984,986,988,990],{"class":34,"line":270},[32,979,833],{"class":69},[32,981,261],{"class":58},[32,983,463],{"class":62},[32,985,153],{"class":69},[32,987,468],{"class":58},[32,989,175],{"class":58},[32,991,572],{"class":69},[32,993,994,996,998],{"class":34,"line":282},[32,995,666],{"class":58},[32,997,669],{"class":58},[32,999,672],{"class":69},[32,1001,1002,1004,1006],{"class":34,"line":291},[32,1003,858],{"class":58},[32,1005,861],{"class":62},[32,1007,864],{"class":69},[32,1009,1010,1012,1015,1017,1019],{"class":34,"line":297},[32,1011,869],{"class":58},[32,1013,1014],{"class":69}," n ",[32,1016,261],{"class":58},[32,1018,618],{"class":58},[32,1020,1021],{"class":69}," in {\n",[32,1023,1024,1026],{"class":34,"line":303},[32,1025,320],{"class":58},[32,1027,70],{"class":69},[32,1029,1030,1032,1034,1036,1038,1041],{"class":34,"line":317},[32,1031,328],{"class":58},[32,1033,892],{"class":69},[32,1035,334],{"class":58},[32,1037,1014],{"class":69},[32,1039,1040],{"class":58},"*",[32,1042,897],{"class":69},[32,1044,1045,1047,1049,1051,1053],{"class":34,"line":325},[32,1046,328],{"class":58},[32,1048,264],{"class":58},[32,1050,347],{"class":69},[32,1052,230],{"class":62},[32,1054,352],{"class":69},[32,1056,1057],{"class":34,"line":340},[32,1058,914],{"class":58},[32,1060,1061],{"class":34,"line":355},[32,1062,294],{"class":69},[32,1064,1065],{"class":34,"line":363},[32,1066,393],{"class":69},[32,1068,1069],{"class":34,"line":368},[32,1070,697],{"class":69},[32,1072,1073,1075],{"class":34,"line":381},[32,1074,760],{"class":58},[32,1076,933],{"class":69},[32,1078,1079],{"class":34,"line":390},[32,1080,94],{"class":69},[32,1082,1083],{"class":34,"line":396},[32,1084,52],{"emptyLinePlaceholder":51},[32,1086,1087,1089,1092,1094,1096,1098,1100,1102,1104,1106,1108,1110,1112,1115,1117,1119,1122,1124,1127,1129,1131,1133],{"class":34,"line":402},[32,1088,147],{"class":58},[32,1090,1091],{"class":62}," filter",[32,1093,153],{"class":69},[32,1095,157],{"class":156},[32,1097,160],{"class":62},[32,1099,163],{"class":69},[32,1101,166],{"class":62},[32,1103,169],{"class":69},[32,1105,963],{"class":156},[32,1107,183],{"class":58},[32,1109,175],{"class":58},[32,1111,169],{"class":69},[32,1113,1114],{"class":156},"pred",[32,1116,669],{"class":58},[32,1118,153],{"class":69},[32,1120,1121],{"class":58},"int",[32,1123,821],{"class":69},[32,1125,1126],{"class":58},"bool",[32,1128,821],{"class":69},[32,1130,824],{"class":58},[32,1132,175],{"class":58},[32,1134,70],{"class":69},[32,1136,1137,1139,1141,1143,1145,1147,1149],{"class":34,"line":407},[32,1138,833],{"class":69},[32,1140,261],{"class":58},[32,1142,463],{"class":62},[32,1144,153],{"class":69},[32,1146,468],{"class":58},[32,1148,175],{"class":58},[32,1150,572],{"class":69},[32,1152,1153,1155,1157],{"class":34,"line":412},[32,1154,666],{"class":58},[32,1156,669],{"class":58},[32,1158,672],{"class":69},[32,1160,1161,1163,1165],{"class":34,"line":455},[32,1162,858],{"class":58},[32,1164,861],{"class":62},[32,1166,864],{"class":69},[32,1168,1169,1171,1173,1175,1177],{"class":34,"line":481},[32,1170,869],{"class":58},[32,1172,1014],{"class":69},[32,1174,261],{"class":58},[32,1176,618],{"class":58},[32,1178,1021],{"class":69},[32,1180,1181,1183,1186],{"class":34,"line":503},[32,1182,273],{"class":58},[32,1184,1185],{"class":62}," pred",[32,1187,1188],{"class":69},"(n) {\n",[32,1190,1191,1194],{"class":34,"line":519},[32,1192,1193],{"class":58},"                select",[32,1195,70],{"class":69},[32,1197,1198,1201,1203,1205],{"class":34,"line":524},[32,1199,1200],{"class":58},"                case",[32,1202,892],{"class":69},[32,1204,334],{"class":58},[32,1206,897],{"class":69},[32,1208,1209,1211,1213,1215,1217],{"class":34,"line":530},[32,1210,1200],{"class":58},[32,1212,264],{"class":58},[32,1214,347],{"class":69},[32,1216,230],{"class":62},[32,1218,352],{"class":69},[32,1220,1221],{"class":34,"line":558},[32,1222,1223],{"class":58},"                    return\n",[32,1225,1226],{"class":34,"line":575},[32,1227,1228],{"class":69},"                }\n",[32,1230,1231],{"class":34,"line":592},[32,1232,294],{"class":69},[32,1234,1235],{"class":34,"line":597},[32,1236,393],{"class":69},[32,1238,1239],{"class":34,"line":602},[32,1240,697],{"class":69},[32,1242,1243,1245],{"class":34,"line":608},[32,1244,760],{"class":58},[32,1246,933],{"class":69},[32,1248,1249],{"class":34,"line":624},[32,1250,94],{"class":69},[32,1252,1253],{"class":34,"line":635},[32,1254,52],{"emptyLinePlaceholder":51},[32,1256,1257,1259,1262],{"class":34,"line":640},[32,1258,147],{"class":58},[32,1260,1261],{"class":62}," main",[32,1263,672],{"class":69},[32,1265,1266,1269,1271,1274,1277,1280,1283],{"class":34,"line":652},[32,1267,1268],{"class":69},"    ctx, cancel ",[32,1270,261],{"class":58},[32,1272,1273],{"class":69}," context.",[32,1275,1276],{"class":62},"WithCancel",[32,1278,1279],{"class":69},"(context.",[32,1281,1282],{"class":62},"Background",[32,1284,1285],{"class":69},"())\n",[32,1287,1288,1290,1293],{"class":34,"line":657},[32,1289,224],{"class":58},[32,1291,1292],{"class":62}," cancel",[32,1294,233],{"class":69},[32,1296,1297],{"class":34,"line":663},[32,1298,52],{"emptyLinePlaceholder":51},[32,1300,1301],{"class":34,"line":675},[32,1302,1303],{"class":38},"    \u002F\u002F Compose: generate → square → filter evens → print\n",[32,1305,1306,1309,1311,1313,1316,1318,1320,1323,1325,1328,1330,1333,1335,1338],{"class":34,"line":685},[32,1307,1308],{"class":69},"    nums ",[32,1310,261],{"class":58},[32,1312,800],{"class":62},[32,1314,1315],{"class":69},"(ctx, ",[32,1317,569],{"class":540},[32,1319,169],{"class":69},[32,1321,1322],{"class":540},"2",[32,1324,169],{"class":69},[32,1326,1327],{"class":540},"3",[32,1329,169],{"class":69},[32,1331,1332],{"class":540},"4",[32,1334,169],{"class":69},[32,1336,1337],{"class":540},"5",[32,1339,572],{"class":69},[32,1341,1342,1345,1347,1349],{"class":34,"line":694},[32,1343,1344],{"class":69},"    squared ",[32,1346,261],{"class":58},[32,1348,948],{"class":62},[32,1350,1351],{"class":69},"(ctx, nums)\n",[32,1353,1354,1357,1359,1361,1364,1366,1368,1371,1373,1375,1377,1380,1383,1386,1389,1391,1394,1396],{"class":34,"line":700},[32,1355,1356],{"class":69},"    evens ",[32,1358,261],{"class":58},[32,1360,1091],{"class":62},[32,1362,1363],{"class":69},"(ctx, squared, ",[32,1365,147],{"class":58},[32,1367,153],{"class":69},[32,1369,1370],{"class":156},"n",[32,1372,175],{"class":58},[32,1374,821],{"class":69},[32,1376,1126],{"class":58},[32,1378,1379],{"class":69}," { ",[32,1381,1382],{"class":58},"return",[32,1384,1385],{"class":69}," n",[32,1387,1388],{"class":58},"%",[32,1390,1322],{"class":540},[32,1392,1393],{"class":58}," ==",[32,1395,541],{"class":540},[32,1397,1398],{"class":69}," })\n",[32,1400,1401],{"class":34,"line":705},[32,1402,52],{"emptyLinePlaceholder":51},[32,1404,1405,1407,1409,1411,1413],{"class":34,"line":711},[32,1406,239],{"class":58},[32,1408,1014],{"class":69},[32,1410,261],{"class":58},[32,1412,618],{"class":58},[32,1414,1415],{"class":69}," evens {\n",[32,1417,1418,1421,1424,1427],{"class":34,"line":722},[32,1419,1420],{"class":69},"        fmt.",[32,1422,1423],{"class":62},"Println",[32,1425,1426],{"class":69},"(n)  ",[32,1428,1429],{"class":38},"\u002F\u002F 4, 16 (2²=4, 4²=16)\n",[32,1431,1432],{"class":34,"line":737},[32,1433,399],{"class":69},[32,1435,1436],{"class":34,"line":752},[32,1437,94],{"class":69},[14,1439,1441],{"id":1440},"fan-out-fan-in-parallel-processing","Fan-Out, Fan-In — Parallel Processing",[19,1443,1444,1824,1831],{"language":21},[23,1445,1447],{"className":25,"code":1446,"language":21,"meta":27,"style":27},"\u002F\u002F Fan-out: distribute work to multiple workers from one input channel.\n\u002F\u002F Fan-in: merge multiple output channels into one.\n\nfunc fanOutFanIn(ctx context.Context, input \u003C-chan int, numWorkers int) \u003C-chan int {\n    \u002F\u002F Fan-out: N goroutines read from the same input, process in parallel:\n    workerOutputs := make([]chan int, numWorkers)\n    for i := 0; i \u003C numWorkers; i++ {\n        workerOutputs[i] = make(chan int)\n        go func(out chan\u003C- int) {\n            defer close(out)\n            for v := range input {  \u002F\u002F shared input — each value goes to ONE worker\n                select {\n                case out \u003C- process(v):\n                case \u003C-ctx.Done():\n                    return\n                }\n            }\n        }(workerOutputs[i])\n    }\n\n    \u002F\u002F Fan-in: merge all worker outputs into one channel:\n    merged := make(chan int)\n    var wg sync.WaitGroup\n    for _, out := range workerOutputs {\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            for v := range out {\n                merged \u003C- v\n            }\n        }()\n    }\n\n    go func() {\n        wg.Wait()\n        close(merged)\n    }()\n\n    return merged\n}\n",[29,1448,1449,1454,1459,1463,1503,1508,1527,1547,1564,1581,1590,1608,1614,1627,1639,1643,1647,1651,1656,1660,1664,1669,1686,1698,1712,1724,1732,1742,1755,1765,1769,1774,1778,1782,1790,1798,1805,1809,1813,1820],{"__ignoreMap":27},[32,1450,1451],{"class":34,"line":35},[32,1452,1453],{"class":38},"\u002F\u002F Fan-out: distribute work to multiple workers from one input channel.\n",[32,1455,1456],{"class":34,"line":42},[32,1457,1458],{"class":38},"\u002F\u002F Fan-in: merge multiple output channels into one.\n",[32,1460,1461],{"class":34,"line":48},[32,1462,52],{"emptyLinePlaceholder":51},[32,1464,1465,1467,1470,1472,1474,1476,1478,1480,1482,1485,1487,1489,1491,1493,1495,1497,1499,1501],{"class":34,"line":55},[32,1466,147],{"class":58},[32,1468,1469],{"class":62}," fanOutFanIn",[32,1471,153],{"class":69},[32,1473,157],{"class":156},[32,1475,160],{"class":62},[32,1477,163],{"class":69},[32,1479,166],{"class":62},[32,1481,169],{"class":69},[32,1483,1484],{"class":156},"input",[32,1486,183],{"class":58},[32,1488,175],{"class":58},[32,1490,169],{"class":69},[32,1492,442],{"class":156},[32,1494,175],{"class":58},[32,1496,821],{"class":69},[32,1498,824],{"class":58},[32,1500,175],{"class":58},[32,1502,70],{"class":69},[32,1504,1505],{"class":34,"line":73},[32,1506,1507],{"class":38},"    \u002F\u002F Fan-out: N goroutines read from the same input, process in parallel:\n",[32,1509,1510,1513,1515,1517,1520,1522,1524],{"class":34,"line":82},[32,1511,1512],{"class":69},"    workerOutputs ",[32,1514,261],{"class":58},[32,1516,463],{"class":62},[32,1518,1519],{"class":69},"([]",[32,1521,468],{"class":58},[32,1523,175],{"class":58},[32,1525,1526],{"class":69},", numWorkers)\n",[32,1528,1529,1531,1533,1535,1537,1539,1541,1543,1545],{"class":34,"line":91},[32,1530,239],{"class":58},[32,1532,535],{"class":69},[32,1534,261],{"class":58},[32,1536,541],{"class":540},[32,1538,544],{"class":69},[32,1540,547],{"class":58},[32,1542,550],{"class":69},[32,1544,553],{"class":58},[32,1546,70],{"class":69},[32,1548,1549,1552,1554,1556,1558,1560,1562],{"class":34,"line":97},[32,1550,1551],{"class":69},"        workerOutputs[i] ",[32,1553,743],{"class":58},[32,1555,463],{"class":62},[32,1557,153],{"class":69},[32,1559,468],{"class":58},[32,1561,175],{"class":58},[32,1563,572],{"class":69},[32,1565,1566,1568,1570,1572,1575,1577,1579],{"class":34,"line":109},[32,1567,578],{"class":58},[32,1569,669],{"class":58},[32,1571,153],{"class":69},[32,1573,1574],{"class":156},"out",[32,1576,193],{"class":58},[32,1578,175],{"class":58},[32,1580,218],{"class":69},[32,1582,1583,1586,1588],{"class":34,"line":117},[32,1584,1585],{"class":58},"            defer",[32,1587,861],{"class":62},[32,1589,864],{"class":69},[32,1591,1592,1595,1598,1600,1602,1605],{"class":34,"line":125},[32,1593,1594],{"class":58},"            for",[32,1596,1597],{"class":69}," v ",[32,1599,261],{"class":58},[32,1601,618],{"class":58},[32,1603,1604],{"class":69}," input {  ",[32,1606,1607],{"class":38},"\u002F\u002F shared input — each value goes to ONE worker\n",[32,1609,1610,1612],{"class":34,"line":134},[32,1611,1193],{"class":58},[32,1613,70],{"class":69},[32,1615,1616,1618,1620,1622,1624],{"class":34,"line":139},[32,1617,1200],{"class":58},[32,1619,892],{"class":69},[32,1621,334],{"class":58},[32,1623,311],{"class":62},[32,1625,1626],{"class":69},"(v):\n",[32,1628,1629,1631,1633,1635,1637],{"class":34,"line":144},[32,1630,1200],{"class":58},[32,1632,264],{"class":58},[32,1634,347],{"class":69},[32,1636,230],{"class":62},[32,1638,352],{"class":69},[32,1640,1641],{"class":34,"line":201},[32,1642,1223],{"class":58},[32,1644,1645],{"class":34,"line":221},[32,1646,1228],{"class":69},[32,1648,1649],{"class":34,"line":236},[32,1650,294],{"class":69},[32,1652,1653],{"class":34,"line":244},[32,1654,1655],{"class":69},"        }(workerOutputs[i])\n",[32,1657,1658],{"class":34,"line":252},[32,1659,399],{"class":69},[32,1661,1662],{"class":34,"line":270},[32,1663,52],{"emptyLinePlaceholder":51},[32,1665,1666],{"class":34,"line":282},[32,1667,1668],{"class":38},"    \u002F\u002F Fan-in: merge all worker outputs into one channel:\n",[32,1670,1671,1674,1676,1678,1680,1682,1684],{"class":34,"line":291},[32,1672,1673],{"class":69},"    merged ",[32,1675,261],{"class":58},[32,1677,463],{"class":62},[32,1679,153],{"class":69},[32,1681,468],{"class":58},[32,1683,175],{"class":58},[32,1685,572],{"class":69},[32,1687,1688,1690,1692,1694,1696],{"class":34,"line":297},[32,1689,506],{"class":58},[32,1691,509],{"class":69},[32,1693,210],{"class":62},[32,1695,163],{"class":69},[32,1697,516],{"class":62},[32,1699,1700,1702,1705,1707,1709],{"class":34,"line":303},[32,1701,239],{"class":58},[32,1703,1704],{"class":69}," _, out ",[32,1706,261],{"class":58},[32,1708,618],{"class":58},[32,1710,1711],{"class":69}," workerOutputs {\n",[32,1713,1714,1716,1718,1720,1722],{"class":34,"line":317},[32,1715,561],{"class":69},[32,1717,564],{"class":62},[32,1719,153],{"class":69},[32,1721,569],{"class":540},[32,1723,572],{"class":69},[32,1725,1726,1728,1730],{"class":34,"line":325},[32,1727,578],{"class":58},[32,1729,669],{"class":58},[32,1731,672],{"class":69},[32,1733,1734,1736,1738,1740],{"class":34,"line":340},[32,1735,1585],{"class":58},[32,1737,227],{"class":69},[32,1739,230],{"class":62},[32,1741,233],{"class":69},[32,1743,1744,1746,1748,1750,1752],{"class":34,"line":355},[32,1745,1594],{"class":58},[32,1747,1597],{"class":69},[32,1749,261],{"class":58},[32,1751,618],{"class":58},[32,1753,1754],{"class":69}," out {\n",[32,1756,1757,1760,1762],{"class":34,"line":363},[32,1758,1759],{"class":69},"                merged ",[32,1761,334],{"class":58},[32,1763,1764],{"class":69}," v\n",[32,1766,1767],{"class":34,"line":368},[32,1768,294],{"class":69},[32,1770,1771],{"class":34,"line":381},[32,1772,1773],{"class":69},"        }()\n",[32,1775,1776],{"class":34,"line":390},[32,1777,399],{"class":69},[32,1779,1780],{"class":34,"line":396},[32,1781,52],{"emptyLinePlaceholder":51},[32,1783,1784,1786,1788],{"class":34,"line":402},[32,1785,666],{"class":58},[32,1787,669],{"class":58},[32,1789,672],{"class":69},[32,1791,1792,1794,1796],{"class":34,"line":407},[32,1793,561],{"class":69},[32,1795,680],{"class":62},[32,1797,233],{"class":69},[32,1799,1800,1802],{"class":34,"line":412},[32,1801,688],{"class":62},[32,1803,1804],{"class":69},"(merged)\n",[32,1806,1807],{"class":34,"line":455},[32,1808,697],{"class":69},[32,1810,1811],{"class":34,"line":481},[32,1812,52],{"emptyLinePlaceholder":51},[32,1814,1815,1817],{"class":34,"line":503},[32,1816,760],{"class":58},[32,1818,1819],{"class":69}," merged\n",[32,1821,1822],{"class":34,"line":519},[32,1823,94],{"class":69},[14,1825,1827,1830],{"id":1826},"errgroupgroup-bounded-concurrent-with-error-handling",[29,1828,1829],{},"errgroup.Group"," — Bounded Concurrent with Error Handling",[19,1832,1833,2137,2141],{"language":21},[23,1834,1836],{"className":25,"code":1835,"language":21,"meta":27,"style":27},"\u002F\u002F import \"golang.org\u002Fx\u002Fsync\u002Ferrgroup\"\n\u002F\u002F errgroup runs goroutines, returns the FIRST error, and cancels the context.\n\nfunc fetchAll(ctx context.Context, urls []string) ([][]byte, error) {\n    g, ctx := errgroup.WithContext(ctx)  \u002F\u002F ctx canceled if any goroutine errors\n    g.SetLimit(10)  \u002F\u002F at most 10 concurrent goroutines (bounded)\n\n    results := make([][]byte, len(urls))  \u002F\u002F each goroutine writes a distinct index\n\n    for i, url := range urls {\n        i, url := i, url  \u002F\u002F capture (pre-1.22)\n        g.Go(func() error {\n            data, err := fetch(ctx, url)\n            if err != nil {\n                return err  \u002F\u002F cancels ctx → other goroutines exit\n            }\n            results[i] = data  \u002F\u002F safe — distinct indices\n            return nil\n        })\n    }\n\n    if err := g.Wait(); err != nil {\n        return nil, err  \u002F\u002F first error from any goroutine\n    }\n    return results, nil\n}\n",[29,1837,1838,1843,1848,1852,1892,1911,1930,1934,1958,1962,1976,1989,2008,2021,2036,2046,2050,2063,2070,2075,2079,2083,2106,2119,2123,2133],{"__ignoreMap":27},[32,1839,1840],{"class":34,"line":35},[32,1841,1842],{"class":38},"\u002F\u002F import \"golang.org\u002Fx\u002Fsync\u002Ferrgroup\"\n",[32,1844,1845],{"class":34,"line":42},[32,1846,1847],{"class":38},"\u002F\u002F errgroup runs goroutines, returns the FIRST error, and cancels the context.\n",[32,1849,1850],{"class":34,"line":48},[32,1851,52],{"emptyLinePlaceholder":51},[32,1853,1854,1856,1859,1861,1863,1865,1867,1869,1871,1874,1876,1879,1882,1885,1887,1890],{"class":34,"line":55},[32,1855,147],{"class":58},[32,1857,1858],{"class":62}," fetchAll",[32,1860,153],{"class":69},[32,1862,157],{"class":156},[32,1864,160],{"class":62},[32,1866,163],{"class":69},[32,1868,166],{"class":62},[32,1870,169],{"class":69},[32,1872,1873],{"class":156},"urls",[32,1875,434],{"class":69},[32,1877,1878],{"class":58},"string",[32,1880,1881],{"class":69},") ([][]",[32,1883,1884],{"class":58},"byte",[32,1886,169],{"class":69},[32,1888,1889],{"class":58},"error",[32,1891,218],{"class":69},[32,1893,1894,1897,1899,1902,1905,1908],{"class":34,"line":73},[32,1895,1896],{"class":69},"    g, ctx ",[32,1898,261],{"class":58},[32,1900,1901],{"class":69}," errgroup.",[32,1903,1904],{"class":62},"WithContext",[32,1906,1907],{"class":69},"(ctx)  ",[32,1909,1910],{"class":38},"\u002F\u002F ctx canceled if any goroutine errors\n",[32,1912,1913,1916,1919,1921,1924,1927],{"class":34,"line":82},[32,1914,1915],{"class":69},"    g.",[32,1917,1918],{"class":62},"SetLimit",[32,1920,153],{"class":69},[32,1922,1923],{"class":540},"10",[32,1925,1926],{"class":69},")  ",[32,1928,1929],{"class":38},"\u002F\u002F at most 10 concurrent goroutines (bounded)\n",[32,1931,1932],{"class":34,"line":91},[32,1933,52],{"emptyLinePlaceholder":51},[32,1935,1936,1939,1941,1943,1946,1948,1950,1952,1955],{"class":34,"line":97},[32,1937,1938],{"class":69},"    results ",[32,1940,261],{"class":58},[32,1942,463],{"class":62},[32,1944,1945],{"class":69},"([][]",[32,1947,1884],{"class":58},[32,1949,169],{"class":69},[32,1951,475],{"class":62},[32,1953,1954],{"class":69},"(urls))  ",[32,1956,1957],{"class":38},"\u002F\u002F each goroutine writes a distinct index\n",[32,1959,1960],{"class":34,"line":109},[32,1961,52],{"emptyLinePlaceholder":51},[32,1963,1964,1966,1969,1971,1973],{"class":34,"line":117},[32,1965,239],{"class":58},[32,1967,1968],{"class":69}," i, url ",[32,1970,261],{"class":58},[32,1972,618],{"class":58},[32,1974,1975],{"class":69}," urls {\n",[32,1977,1978,1981,1983,1986],{"class":34,"line":125},[32,1979,1980],{"class":69},"        i, url ",[32,1982,261],{"class":58},[32,1984,1985],{"class":69}," i, url  ",[32,1987,1988],{"class":38},"\u002F\u002F capture (pre-1.22)\n",[32,1990,1991,1994,1997,1999,2001,2004,2006],{"class":34,"line":134},[32,1992,1993],{"class":69},"        g.",[32,1995,1996],{"class":62},"Go",[32,1998,153],{"class":69},[32,2000,147],{"class":58},[32,2002,2003],{"class":69},"() ",[32,2005,1889],{"class":58},[32,2007,70],{"class":69},[32,2009,2010,2013,2015,2018],{"class":34,"line":139},[32,2011,2012],{"class":69},"            data, err ",[32,2014,261],{"class":58},[32,2016,2017],{"class":62}," fetch",[32,2019,2020],{"class":69},"(ctx, url)\n",[32,2022,2023,2025,2028,2031,2034],{"class":34,"line":144},[32,2024,273],{"class":58},[32,2026,2027],{"class":69}," err ",[32,2029,2030],{"class":58},"!=",[32,2032,2033],{"class":540}," nil",[32,2035,70],{"class":69},[32,2037,2038,2040,2043],{"class":34,"line":201},[32,2039,285],{"class":58},[32,2041,2042],{"class":69}," err  ",[32,2044,2045],{"class":38},"\u002F\u002F cancels ctx → other goroutines exit\n",[32,2047,2048],{"class":34,"line":221},[32,2049,294],{"class":69},[32,2051,2052,2055,2057,2060],{"class":34,"line":236},[32,2053,2054],{"class":69},"            results[i] ",[32,2056,743],{"class":58},[32,2058,2059],{"class":69}," data  ",[32,2061,2062],{"class":38},"\u002F\u002F safe — distinct indices\n",[32,2064,2065,2067],{"class":34,"line":244},[32,2066,384],{"class":58},[32,2068,2069],{"class":540}," nil\n",[32,2071,2072],{"class":34,"line":252},[32,2073,2074],{"class":69},"        })\n",[32,2076,2077],{"class":34,"line":270},[32,2078,399],{"class":69},[32,2080,2081],{"class":34,"line":282},[32,2082,52],{"emptyLinePlaceholder":51},[32,2084,2085,2088,2090,2092,2095,2097,2100,2102,2104],{"class":34,"line":291},[32,2086,2087],{"class":58},"    if",[32,2089,2027],{"class":69},[32,2091,261],{"class":58},[32,2093,2094],{"class":69}," g.",[32,2096,680],{"class":62},[32,2098,2099],{"class":69},"(); err ",[32,2101,2030],{"class":58},[32,2103,2033],{"class":540},[32,2105,70],{"class":69},[32,2107,2108,2111,2113,2116],{"class":34,"line":297},[32,2109,2110],{"class":58},"        return",[32,2112,2033],{"class":540},[32,2114,2115],{"class":69},", err  ",[32,2117,2118],{"class":38},"\u002F\u002F first error from any goroutine\n",[32,2120,2121],{"class":34,"line":303},[32,2122,399],{"class":69},[32,2124,2125,2127,2130],{"class":34,"line":317},[32,2126,760],{"class":58},[32,2128,2129],{"class":69}," results, ",[32,2131,2132],{"class":540},"nil\n",[32,2134,2135],{"class":34,"line":325},[32,2136,94],{"class":69},[14,2138,2140],{"id":2139},"semaphore-bounded-concurrency-without-a-worker-pool","Semaphore — Bounded Concurrency Without a Worker Pool",[19,2142,2143,2423,2427],{"language":21},[23,2144,2146],{"className":25,"code":2145,"language":21,"meta":27,"style":27},"\u002F\u002F import \"golang.org\u002Fx\u002Fsync\u002Fsemaphore\"\n\u002F\u002F A semaphore limits concurrent goroutines without a fixed worker pool.\n\u002F\u002F Each goroutine acquires a token; releasing returns it to the pool.\n\nfunc processWithLimit(ctx context.Context, items []int, maxConcurrent int) error {\n    sem := semaphore.NewWeighted(int64(maxConcurrent))\n    var wg sync.WaitGroup\n\n    for _, item := range items {\n        wg.Add(1)\n        go func(item int) {\n            defer wg.Done()\n\n            \u002F\u002F Acquire a token (blocks if maxConcurrent are in use):\n            if err := sem.Acquire(ctx, 1); err != nil {\n                return  \u002F\u002F ctx canceled\n            }\n            defer sem.Release(1)\n\n            process(item)  \u002F\u002F at most maxConcurrent goroutines here simultaneously\n        }(item)\n    }\n    wg.Wait()\n    return nil\n}\n\n\u002F\u002F Use a semaphore when:\n\u002F\u002F   - You want bounded concurrency but the \"jobs\" are dynamically generated\n\u002F\u002F   - You don't want to pre-allocate a channel buffer\n\u002F\u002F   - Each goroutine needs to do setup\u002Fteardown around the work\n",[29,2147,2148,2153,2158,2163,2167,2206,2227,2239,2243,2257,2269,2284,2294,2298,2303,2330,2337,2341,2356,2360,2371,2376,2380,2389,2395,2399,2403,2408,2413,2418],{"__ignoreMap":27},[32,2149,2150],{"class":34,"line":35},[32,2151,2152],{"class":38},"\u002F\u002F import \"golang.org\u002Fx\u002Fsync\u002Fsemaphore\"\n",[32,2154,2155],{"class":34,"line":42},[32,2156,2157],{"class":38},"\u002F\u002F A semaphore limits concurrent goroutines without a fixed worker pool.\n",[32,2159,2160],{"class":34,"line":48},[32,2161,2162],{"class":38},"\u002F\u002F Each goroutine acquires a token; releasing returns it to the pool.\n",[32,2164,2165],{"class":34,"line":55},[32,2166,52],{"emptyLinePlaceholder":51},[32,2168,2169,2171,2174,2176,2178,2180,2182,2184,2186,2189,2191,2193,2195,2198,2200,2202,2204],{"class":34,"line":73},[32,2170,147],{"class":58},[32,2172,2173],{"class":62}," processWithLimit",[32,2175,153],{"class":69},[32,2177,157],{"class":156},[32,2179,160],{"class":62},[32,2181,163],{"class":69},[32,2183,166],{"class":62},[32,2185,169],{"class":69},[32,2187,2188],{"class":156},"items",[32,2190,434],{"class":69},[32,2192,1121],{"class":58},[32,2194,169],{"class":69},[32,2196,2197],{"class":156},"maxConcurrent",[32,2199,175],{"class":58},[32,2201,821],{"class":69},[32,2203,1889],{"class":58},[32,2205,70],{"class":69},[32,2207,2208,2211,2213,2216,2219,2221,2224],{"class":34,"line":82},[32,2209,2210],{"class":69},"    sem ",[32,2212,261],{"class":58},[32,2214,2215],{"class":69}," semaphore.",[32,2217,2218],{"class":62},"NewWeighted",[32,2220,153],{"class":69},[32,2222,2223],{"class":58},"int64",[32,2225,2226],{"class":69},"(maxConcurrent))\n",[32,2228,2229,2231,2233,2235,2237],{"class":34,"line":91},[32,2230,506],{"class":58},[32,2232,509],{"class":69},[32,2234,210],{"class":62},[32,2236,163],{"class":69},[32,2238,516],{"class":62},[32,2240,2241],{"class":34,"line":97},[32,2242,52],{"emptyLinePlaceholder":51},[32,2244,2245,2247,2250,2252,2254],{"class":34,"line":109},[32,2246,239],{"class":58},[32,2248,2249],{"class":69}," _, item ",[32,2251,261],{"class":58},[32,2253,618],{"class":58},[32,2255,2256],{"class":69}," items {\n",[32,2258,2259,2261,2263,2265,2267],{"class":34,"line":117},[32,2260,561],{"class":69},[32,2262,564],{"class":62},[32,2264,153],{"class":69},[32,2266,569],{"class":540},[32,2268,572],{"class":69},[32,2270,2271,2273,2275,2277,2280,2282],{"class":34,"line":125},[32,2272,578],{"class":58},[32,2274,669],{"class":58},[32,2276,153],{"class":69},[32,2278,2279],{"class":156},"item",[32,2281,175],{"class":58},[32,2283,218],{"class":69},[32,2285,2286,2288,2290,2292],{"class":34,"line":134},[32,2287,1585],{"class":58},[32,2289,227],{"class":69},[32,2291,230],{"class":62},[32,2293,233],{"class":69},[32,2295,2296],{"class":34,"line":139},[32,2297,52],{"emptyLinePlaceholder":51},[32,2299,2300],{"class":34,"line":144},[32,2301,2302],{"class":38},"            \u002F\u002F Acquire a token (blocks if maxConcurrent are in use):\n",[32,2304,2305,2307,2309,2311,2314,2317,2319,2321,2324,2326,2328],{"class":34,"line":201},[32,2306,273],{"class":58},[32,2308,2027],{"class":69},[32,2310,261],{"class":58},[32,2312,2313],{"class":69}," sem.",[32,2315,2316],{"class":62},"Acquire",[32,2318,1315],{"class":69},[32,2320,569],{"class":540},[32,2322,2323],{"class":69},"); err ",[32,2325,2030],{"class":58},[32,2327,2033],{"class":540},[32,2329,70],{"class":69},[32,2331,2332,2334],{"class":34,"line":221},[32,2333,285],{"class":58},[32,2335,2336],{"class":38},"  \u002F\u002F ctx canceled\n",[32,2338,2339],{"class":34,"line":236},[32,2340,294],{"class":69},[32,2342,2343,2345,2347,2350,2352,2354],{"class":34,"line":244},[32,2344,1585],{"class":58},[32,2346,2313],{"class":69},[32,2348,2349],{"class":62},"Release",[32,2351,153],{"class":69},[32,2353,569],{"class":540},[32,2355,572],{"class":69},[32,2357,2358],{"class":34,"line":252},[32,2359,52],{"emptyLinePlaceholder":51},[32,2361,2362,2365,2368],{"class":34,"line":270},[32,2363,2364],{"class":62},"            process",[32,2366,2367],{"class":69},"(item)  ",[32,2369,2370],{"class":38},"\u002F\u002F at most maxConcurrent goroutines here simultaneously\n",[32,2372,2373],{"class":34,"line":282},[32,2374,2375],{"class":69},"        }(item)\n",[32,2377,2378],{"class":34,"line":291},[32,2379,399],{"class":69},[32,2381,2382,2385,2387],{"class":34,"line":297},[32,2383,2384],{"class":69},"    wg.",[32,2386,680],{"class":62},[32,2388,233],{"class":69},[32,2390,2391,2393],{"class":34,"line":303},[32,2392,760],{"class":58},[32,2394,2069],{"class":540},[32,2396,2397],{"class":34,"line":317},[32,2398,94],{"class":69},[32,2400,2401],{"class":34,"line":325},[32,2402,52],{"emptyLinePlaceholder":51},[32,2404,2405],{"class":34,"line":340},[32,2406,2407],{"class":38},"\u002F\u002F Use a semaphore when:\n",[32,2409,2410],{"class":34,"line":355},[32,2411,2412],{"class":38},"\u002F\u002F   - You want bounded concurrency but the \"jobs\" are dynamically generated\n",[32,2414,2415],{"class":34,"line":363},[32,2416,2417],{"class":38},"\u002F\u002F   - You don't want to pre-allocate a channel buffer\n",[32,2419,2420],{"class":34,"line":368},[32,2421,2422],{"class":38},"\u002F\u002F   - Each goroutine needs to do setup\u002Fteardown around the work\n",[14,2424,2426],{"id":2425},"generator-lazy-stream","Generator — Lazy Stream",[19,2428,2429,2706,2710],{"language":21},[23,2430,2432],{"className":25,"code":2431,"language":21,"meta":27,"style":27},"\u002F\u002F Generator: a function that returns a channel, producing values lazily.\n\u002F\u002F Values are generated on-demand — the next value isn't computed until\n\u002F\u002F someone receives.\n\nfunc fibonacci(ctx context.Context) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        a, b := 0, 1\n        for {\n            select {\n            case out \u003C- a:\n                a, b = b, a+b\n            case \u003C-ctx.Done():\n                return\n            }\n        }\n    }()\n    return out\n}\n\nfunc main() {\n    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond)\n    defer cancel()\n\n    for n := range fibonacci(ctx) {\n        fmt.Println(n)\n        \u002F\u002F 0 1 1 2 3 5 8 13 21 34 55 ... (stops after 10ms)\n    }\n}\n\n\u002F\u002F ⚠️ Generators MUST have an exit path (ctx.Done()) — otherwise\n\u002F\u002F the goroutine leaks if the consumer stops reading before the generator\n\u002F\u002F is done. The `for n := range` exits when ctx is canceled → out is closed.\n",[29,2433,2434,2439,2444,2449,2453,2478,2494,2502,2510,2524,2530,2536,2547,2563,2575,2579,2583,2587,2591,2597,2601,2605,2613,2638,2646,2650,2665,2674,2679,2683,2687,2691,2696,2701],{"__ignoreMap":27},[32,2435,2436],{"class":34,"line":35},[32,2437,2438],{"class":38},"\u002F\u002F Generator: a function that returns a channel, producing values lazily.\n",[32,2440,2441],{"class":34,"line":42},[32,2442,2443],{"class":38},"\u002F\u002F Values are generated on-demand — the next value isn't computed until\n",[32,2445,2446],{"class":34,"line":48},[32,2447,2448],{"class":38},"\u002F\u002F someone receives.\n",[32,2450,2451],{"class":34,"line":55},[32,2452,52],{"emptyLinePlaceholder":51},[32,2454,2455,2457,2460,2462,2464,2466,2468,2470,2472,2474,2476],{"class":34,"line":73},[32,2456,147],{"class":58},[32,2458,2459],{"class":62}," fibonacci",[32,2461,153],{"class":69},[32,2463,157],{"class":156},[32,2465,160],{"class":62},[32,2467,163],{"class":69},[32,2469,166],{"class":62},[32,2471,821],{"class":69},[32,2473,824],{"class":58},[32,2475,175],{"class":58},[32,2477,70],{"class":69},[32,2479,2480,2482,2484,2486,2488,2490,2492],{"class":34,"line":82},[32,2481,833],{"class":69},[32,2483,261],{"class":58},[32,2485,463],{"class":62},[32,2487,153],{"class":69},[32,2489,468],{"class":58},[32,2491,175],{"class":58},[32,2493,572],{"class":69},[32,2495,2496,2498,2500],{"class":34,"line":91},[32,2497,666],{"class":58},[32,2499,669],{"class":58},[32,2501,672],{"class":69},[32,2503,2504,2506,2508],{"class":34,"line":97},[32,2505,858],{"class":58},[32,2507,861],{"class":62},[32,2509,864],{"class":69},[32,2511,2512,2515,2517,2519,2521],{"class":34,"line":109},[32,2513,2514],{"class":69},"        a, b ",[32,2516,261],{"class":58},[32,2518,541],{"class":540},[32,2520,169],{"class":69},[32,2522,2523],{"class":540},"1\n",[32,2525,2526,2528],{"class":34,"line":117},[32,2527,869],{"class":58},[32,2529,70],{"class":69},[32,2531,2532,2534],{"class":34,"line":125},[32,2533,320],{"class":58},[32,2535,70],{"class":69},[32,2537,2538,2540,2542,2544],{"class":34,"line":134},[32,2539,328],{"class":58},[32,2541,892],{"class":69},[32,2543,334],{"class":58},[32,2545,2546],{"class":69}," a:\n",[32,2548,2549,2552,2554,2557,2560],{"class":34,"line":139},[32,2550,2551],{"class":69},"                a, b ",[32,2553,743],{"class":58},[32,2555,2556],{"class":69}," b, a",[32,2558,2559],{"class":58},"+",[32,2561,2562],{"class":69},"b\n",[32,2564,2565,2567,2569,2571,2573],{"class":34,"line":144},[32,2566,328],{"class":58},[32,2568,264],{"class":58},[32,2570,347],{"class":69},[32,2572,230],{"class":62},[32,2574,352],{"class":69},[32,2576,2577],{"class":34,"line":201},[32,2578,914],{"class":58},[32,2580,2581],{"class":34,"line":221},[32,2582,294],{"class":69},[32,2584,2585],{"class":34,"line":236},[32,2586,393],{"class":69},[32,2588,2589],{"class":34,"line":244},[32,2590,697],{"class":69},[32,2592,2593,2595],{"class":34,"line":252},[32,2594,760],{"class":58},[32,2596,933],{"class":69},[32,2598,2599],{"class":34,"line":270},[32,2600,94],{"class":69},[32,2602,2603],{"class":34,"line":282},[32,2604,52],{"emptyLinePlaceholder":51},[32,2606,2607,2609,2611],{"class":34,"line":291},[32,2608,147],{"class":58},[32,2610,1261],{"class":62},[32,2612,672],{"class":69},[32,2614,2615,2617,2619,2621,2624,2626,2628,2631,2633,2635],{"class":34,"line":297},[32,2616,1268],{"class":69},[32,2618,261],{"class":58},[32,2620,1273],{"class":69},[32,2622,2623],{"class":62},"WithTimeout",[32,2625,1279],{"class":69},[32,2627,1282],{"class":62},[32,2629,2630],{"class":69},"(), ",[32,2632,1923],{"class":540},[32,2634,1040],{"class":58},[32,2636,2637],{"class":69},"time.Millisecond)\n",[32,2639,2640,2642,2644],{"class":34,"line":303},[32,2641,224],{"class":58},[32,2643,1292],{"class":62},[32,2645,233],{"class":69},[32,2647,2648],{"class":34,"line":317},[32,2649,52],{"emptyLinePlaceholder":51},[32,2651,2652,2654,2656,2658,2660,2662],{"class":34,"line":325},[32,2653,239],{"class":58},[32,2655,1014],{"class":69},[32,2657,261],{"class":58},[32,2659,618],{"class":58},[32,2661,2459],{"class":62},[32,2663,2664],{"class":69},"(ctx) {\n",[32,2666,2667,2669,2671],{"class":34,"line":340},[32,2668,1420],{"class":69},[32,2670,1423],{"class":62},[32,2672,2673],{"class":69},"(n)\n",[32,2675,2676],{"class":34,"line":355},[32,2677,2678],{"class":38},"        \u002F\u002F 0 1 1 2 3 5 8 13 21 34 55 ... (stops after 10ms)\n",[32,2680,2681],{"class":34,"line":363},[32,2682,399],{"class":69},[32,2684,2685],{"class":34,"line":368},[32,2686,94],{"class":69},[32,2688,2689],{"class":34,"line":381},[32,2690,52],{"emptyLinePlaceholder":51},[32,2692,2693],{"class":34,"line":390},[32,2694,2695],{"class":38},"\u002F\u002F ⚠️ Generators MUST have an exit path (ctx.Done()) — otherwise\n",[32,2697,2698],{"class":34,"line":396},[32,2699,2700],{"class":38},"\u002F\u002F the goroutine leaks if the consumer stops reading before the generator\n",[32,2702,2703],{"class":34,"line":402},[32,2704,2705],{"class":38},"\u002F\u002F is done. The `for n := range` exits when ctx is canceled → out is closed.\n",[14,2707,2709],{"id":2708},"graceful-shutdown-production-pattern","Graceful Shutdown — Production Pattern",[19,2711,2712,2979,2983,3063,3067,3163,3167],{"language":21},[23,2713,2715],{"className":25,"code":2714,"language":21,"meta":27,"style":27},"func runServer(ctx context.Context) error {\n    \u002F\u002F Start workers:\n    jobs := make(chan Job, 100)\n    go startProducer(ctx, jobs)\n\n    \u002F\u002F Process jobs with a worker pool:\n    results := runWorkerPool(ctx, jobs, 10)\n\n    \u002F\u002F Wait for context cancellation (SIGINT\u002FSIGTERM):\n    \u003C-ctx.Done()\n    log.Println(\"shutdown signal received, draining...\")\n\n    \u002F\u002F The workers' select includes \u003C-ctx.Done() — they exit cleanly.\n    \u002F\u002F Channels are closed by the goroutines, ranges end, results are collected.\n\n    return nil\n}\n\nfunc main() {\n    ctx, stop := signal.NotifyContext(context.Background(),\n        syscall.SIGINT, syscall.SIGTERM)\n    defer stop()\n\n    if err := runServer(ctx); err != nil &&\n        !errors.Is(err, context.Canceled) {\n        log.Fatal(err)\n    }\n    log.Println(\"server stopped\")\n}\n",[29,2716,2717,2740,2744,2766,2776,2780,2785,2800,2804,2809,2820,2835,2839,2844,2849,2853,2859,2863,2867,2875,2895,2900,2909,2913,2933,2947,2958,2962,2975],{"__ignoreMap":27},[32,2718,2719,2721,2724,2726,2728,2730,2732,2734,2736,2738],{"class":34,"line":35},[32,2720,147],{"class":58},[32,2722,2723],{"class":62}," runServer",[32,2725,153],{"class":69},[32,2727,157],{"class":156},[32,2729,160],{"class":62},[32,2731,163],{"class":69},[32,2733,166],{"class":62},[32,2735,821],{"class":69},[32,2737,1889],{"class":58},[32,2739,70],{"class":69},[32,2741,2742],{"class":34,"line":42},[32,2743,527],{"class":38},[32,2745,2746,2749,2751,2753,2755,2757,2759,2761,2764],{"class":34,"line":48},[32,2747,2748],{"class":69},"    jobs ",[32,2750,261],{"class":58},[32,2752,463],{"class":62},[32,2754,153],{"class":69},[32,2756,468],{"class":58},[32,2758,63],{"class":62},[32,2760,169],{"class":69},[32,2762,2763],{"class":540},"100",[32,2765,572],{"class":69},[32,2767,2768,2770,2773],{"class":34,"line":55},[32,2769,666],{"class":58},[32,2771,2772],{"class":62}," startProducer",[32,2774,2775],{"class":69},"(ctx, jobs)\n",[32,2777,2778],{"class":34,"line":73},[32,2779,52],{"emptyLinePlaceholder":51},[32,2781,2782],{"class":34,"line":82},[32,2783,2784],{"class":38},"    \u002F\u002F Process jobs with a worker pool:\n",[32,2786,2787,2789,2791,2793,2796,2798],{"class":34,"line":91},[32,2788,1938],{"class":69},[32,2790,261],{"class":58},[32,2792,417],{"class":62},[32,2794,2795],{"class":69},"(ctx, jobs, ",[32,2797,1923],{"class":540},[32,2799,572],{"class":69},[32,2801,2802],{"class":34,"line":97},[32,2803,52],{"emptyLinePlaceholder":51},[32,2805,2806],{"class":34,"line":109},[32,2807,2808],{"class":38},"    \u002F\u002F Wait for context cancellation (SIGINT\u002FSIGTERM):\n",[32,2810,2811,2814,2816,2818],{"class":34,"line":117},[32,2812,2813],{"class":58},"    \u003C-",[32,2815,347],{"class":69},[32,2817,230],{"class":62},[32,2819,233],{"class":69},[32,2821,2822,2825,2827,2829,2833],{"class":34,"line":125},[32,2823,2824],{"class":69},"    log.",[32,2826,1423],{"class":62},[32,2828,153],{"class":69},[32,2830,2832],{"class":2831},"sJ6F3","\"shutdown signal received, draining...\"",[32,2834,572],{"class":69},[32,2836,2837],{"class":34,"line":134},[32,2838,52],{"emptyLinePlaceholder":51},[32,2840,2841],{"class":34,"line":139},[32,2842,2843],{"class":38},"    \u002F\u002F The workers' select includes \u003C-ctx.Done() — they exit cleanly.\n",[32,2845,2846],{"class":34,"line":144},[32,2847,2848],{"class":38},"    \u002F\u002F Channels are closed by the goroutines, ranges end, results are collected.\n",[32,2850,2851],{"class":34,"line":201},[32,2852,52],{"emptyLinePlaceholder":51},[32,2854,2855,2857],{"class":34,"line":221},[32,2856,760],{"class":58},[32,2858,2069],{"class":540},[32,2860,2861],{"class":34,"line":236},[32,2862,94],{"class":69},[32,2864,2865],{"class":34,"line":244},[32,2866,52],{"emptyLinePlaceholder":51},[32,2868,2869,2871,2873],{"class":34,"line":252},[32,2870,147],{"class":58},[32,2872,1261],{"class":62},[32,2874,672],{"class":69},[32,2876,2877,2880,2882,2885,2888,2890,2892],{"class":34,"line":270},[32,2878,2879],{"class":69},"    ctx, stop ",[32,2881,261],{"class":58},[32,2883,2884],{"class":69}," signal.",[32,2886,2887],{"class":62},"NotifyContext",[32,2889,1279],{"class":69},[32,2891,1282],{"class":62},[32,2893,2894],{"class":69},"(),\n",[32,2896,2897],{"class":34,"line":282},[32,2898,2899],{"class":69},"        syscall.SIGINT, syscall.SIGTERM)\n",[32,2901,2902,2904,2907],{"class":34,"line":291},[32,2903,224],{"class":58},[32,2905,2906],{"class":62}," stop",[32,2908,233],{"class":69},[32,2910,2911],{"class":34,"line":297},[32,2912,52],{"emptyLinePlaceholder":51},[32,2914,2915,2917,2919,2921,2923,2926,2928,2930],{"class":34,"line":303},[32,2916,2087],{"class":58},[32,2918,2027],{"class":69},[32,2920,261],{"class":58},[32,2922,2723],{"class":62},[32,2924,2925],{"class":69},"(ctx); err ",[32,2927,2030],{"class":58},[32,2929,2033],{"class":540},[32,2931,2932],{"class":58}," &&\n",[32,2934,2935,2938,2941,2944],{"class":34,"line":317},[32,2936,2937],{"class":58},"        !",[32,2939,2940],{"class":69},"errors.",[32,2942,2943],{"class":62},"Is",[32,2945,2946],{"class":69},"(err, context.Canceled) {\n",[32,2948,2949,2952,2955],{"class":34,"line":325},[32,2950,2951],{"class":69},"        log.",[32,2953,2954],{"class":62},"Fatal",[32,2956,2957],{"class":69},"(err)\n",[32,2959,2960],{"class":34,"line":340},[32,2961,399],{"class":69},[32,2963,2964,2966,2968,2970,2973],{"class":34,"line":355},[32,2965,2824],{"class":69},[32,2967,1423],{"class":62},[32,2969,153],{"class":69},[32,2971,2972],{"class":2831},"\"server stopped\"",[32,2974,572],{"class":69},[32,2976,2977],{"class":34,"line":363},[32,2978,94],{"class":69},[14,2980,2982],{"id":2981},"tips-tricks","💡 Tips & Tricks",[2984,2985,2986,2998,3011,3032,3044,3052],"ul",{},[2987,2988,2989,2993,2994,2997],"li",{},[2990,2991,2992],"strong",{},"Idiom",": use a worker pool for bounded concurrency — unbounded ",[29,2995,2996],{},"go f()"," in a loop can create millions of goroutines, exhausting memory and overwhelming downstream resources (DB connections, API rate limits). A fixed pool of N workers processes jobs from a channel.",[2987,2999,3000,3002,3003,3006,3007,3010],{},[2990,3001,2992],{},": each pipeline stage closes its output channel when done — ",[29,3004,3005],{},"defer close(out)"," in the goroutine. The next stage's ",[29,3008,3009],{},"range"," exits cleanly when the input channel is closed.",[2987,3012,3013,3015,3016,3019,3020,3023,3024,3027,3028,3031],{},[2990,3014,2992],{},": use ",[29,3017,3018],{},"errgroup.WithContext"," for concurrent error handling — ",[29,3021,3022],{},"g.Go"," returns an error → the context is canceled → other goroutines see ",[29,3025,3026],{},"\u003C-ctx.Done()"," and exit. ",[29,3029,3030],{},"g.Wait()"," returns the first error.",[2987,3033,3034,3036,3037,3040,3041,3043],{},[2990,3035,2992],{},": use a semaphore (",[29,3038,3039],{},"golang.org\u002Fx\u002Fsync\u002Fsemaphore",") for bounded concurrency without a worker pool — each goroutine acquires a token; ",[29,3042,1918],{}," on errgroup is even simpler.",[2987,3045,3046,3048,3049,3051],{},[2990,3047,2992],{},": every concurrent goroutine MUST have an exit path (",[29,3050,3026],{},") — without it, the goroutine leaks if it blocks forever. Generators, workers, pipelines all need cancellation.",[2987,3053,3054,3015,3056,3059,3060,163],{},[2990,3055,2992],{},[29,3057,3058],{},"signal.NotifyContext"," for graceful shutdown — the context is canceled on SIGINT\u002FSIGTERM, propagating to all goroutines that select on ",[29,3061,3062],{},"ctx.Done()",[14,3064,3066],{"id":3065},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2984,3068,3069,3079,3085,3100,3118,3132,3142,3152],{},[2987,3070,3071,3074,3075,3078],{},[2990,3072,3073],{},"Unbounded fan-out exhausts resources",": ",[29,3076,3077],{},"for _, item := range huge { go process(item) }"," creates len(huge) goroutines at once. Use a worker pool or semaphore for bounded concurrency.",[2987,3080,3081,3084],{},[2990,3082,3083],{},"Pipeline deadlock with unbuffered channels",": if stage A sends and stage B isn't ready, A blocks. Use buffered channels or ensure stages run concurrently.",[2987,3086,3087,3074,3090,3093,3094,3097,3098,163],{},[2990,3088,3089],{},"Forgetting to close the output channel",[29,3091,3092],{},"for v := range ch"," never returns unless ",[29,3095,3096],{},"ch"," is closed. The range goroutine leaks. Use ",[29,3099,3005],{},[2987,3101,3102,3074,3105,3108,3109,3111,3112,3115,3116,163],{},[2990,3103,3104],{},"Generator without exit path leaks",[29,3106,3107],{},"func gen() \u003C-chan int { ch := make(chan int); go func() { for { ch \u003C- compute() } }(); return ch }"," — leaks if the consumer stops reading. Pass ",[29,3110,157],{}," and ",[29,3113,3114],{},"select"," on ",[29,3117,3062],{},[2987,3119,3120,3074,3126,3128,3129,3131],{},[2990,3121,3122,3125],{},[29,3123,3124],{},"errgroup"," cancels on first error",[29,3127,3022],{}," returning an error cancels the context. Other goroutines see ",[29,3130,3026],{}," and may exit early. This is by design (coordinated failure).",[2987,3133,3134,3137,3138,3141],{},[2990,3135,3136],{},"Worker pool with unbuffered results channel",": if no one reads results, workers block on ",[29,3139,3140],{},"results \u003C- r"," → deadlock. Buffer the results channel or collect in a separate goroutine.",[2987,3143,3144,3147,3148,3151],{},[2990,3145,3146],{},"Goroutine leak in fan-in",": if the merged output isn't fully consumed, the fan-in goroutines block on ",[29,3149,3150],{},"merged \u003C- v"," forever. Ensure the consumer drains the output or provide a cancellation path.",[2987,3153,3154,3159,3160,3162],{},[2990,3155,3156,3158],{},[29,3157,3114],{}," in worker doesn't guarantee fairness",": if multiple cases are ready, ",[29,3161,3114],{}," picks randomly. A always-ready case can starve others. Use separate goroutines or explicit scheduling.",[14,3164,3166],{"id":3165},"quick-quiz","🧠 Quick Quiz",[19,3168,3169,3427],{"language":21},[23,3170,3172],{"className":25,"code":3171,"language":21,"meta":27,"style":27},"func worker(jobs \u003C-chan int, results chan\u003C- int) {\n    for j := range jobs {\n        results \u003C- j * j\n    }\n}\n\nfunc main() {\n    jobs := make(chan int, 100)\n    results := make(chan int, 100)\n\n    for w := 0; w \u003C 3; w++ {\n        go worker(jobs, results)\n    }\n\n    for j := 1; j \u003C= 5; j++ {\n        jobs \u003C- j\n    }\n    close(jobs)\n\n    for r := 0; r \u003C 5; r++ {\n        fmt.Println(\u003C-results)\n    }\n}\n",[29,3173,3174,3198,3211,3224,3228,3232,3236,3244,3264,3284,3288,3314,3323,3327,3331,3358,3367,3371,3378,3382,3406,3419,3423],{"__ignoreMap":27},[32,3175,3176,3178,3180,3182,3184,3186,3188,3190,3192,3194,3196],{"class":34,"line":35},[32,3177,147],{"class":58},[32,3179,150],{"class":62},[32,3181,153],{"class":69},[32,3183,180],{"class":156},[32,3185,183],{"class":58},[32,3187,175],{"class":58},[32,3189,169],{"class":69},[32,3191,190],{"class":156},[32,3193,193],{"class":58},[32,3195,175],{"class":58},[32,3197,218],{"class":69},[32,3199,3200,3202,3205,3207,3209],{"class":34,"line":42},[32,3201,239],{"class":58},[32,3203,3204],{"class":69}," j ",[32,3206,261],{"class":58},[32,3208,618],{"class":58},[32,3210,621],{"class":69},[32,3212,3213,3215,3217,3219,3221],{"class":34,"line":48},[32,3214,740],{"class":69},[32,3216,334],{"class":58},[32,3218,3204],{"class":69},[32,3220,1040],{"class":58},[32,3222,3223],{"class":69}," j\n",[32,3225,3226],{"class":34,"line":55},[32,3227,399],{"class":69},[32,3229,3230],{"class":34,"line":73},[32,3231,94],{"class":69},[32,3233,3234],{"class":34,"line":82},[32,3235,52],{"emptyLinePlaceholder":51},[32,3237,3238,3240,3242],{"class":34,"line":91},[32,3239,147],{"class":58},[32,3241,1261],{"class":62},[32,3243,672],{"class":69},[32,3245,3246,3248,3250,3252,3254,3256,3258,3260,3262],{"class":34,"line":97},[32,3247,2748],{"class":69},[32,3249,261],{"class":58},[32,3251,463],{"class":62},[32,3253,153],{"class":69},[32,3255,468],{"class":58},[32,3257,175],{"class":58},[32,3259,169],{"class":69},[32,3261,2763],{"class":540},[32,3263,572],{"class":69},[32,3265,3266,3268,3270,3272,3274,3276,3278,3280,3282],{"class":34,"line":109},[32,3267,1938],{"class":69},[32,3269,261],{"class":58},[32,3271,463],{"class":62},[32,3273,153],{"class":69},[32,3275,468],{"class":58},[32,3277,175],{"class":58},[32,3279,169],{"class":69},[32,3281,2763],{"class":540},[32,3283,572],{"class":69},[32,3285,3286],{"class":34,"line":117},[32,3287,52],{"emptyLinePlaceholder":51},[32,3289,3290,3292,3295,3297,3299,3302,3304,3307,3310,3312],{"class":34,"line":125},[32,3291,239],{"class":58},[32,3293,3294],{"class":69}," w ",[32,3296,261],{"class":58},[32,3298,541],{"class":540},[32,3300,3301],{"class":69},"; w ",[32,3303,547],{"class":58},[32,3305,3306],{"class":540}," 3",[32,3308,3309],{"class":69},"; w",[32,3311,553],{"class":58},[32,3313,70],{"class":69},[32,3315,3316,3318,3320],{"class":34,"line":134},[32,3317,578],{"class":58},[32,3319,150],{"class":62},[32,3321,3322],{"class":69},"(jobs, results)\n",[32,3324,3325],{"class":34,"line":139},[32,3326,399],{"class":69},[32,3328,3329],{"class":34,"line":144},[32,3330,52],{"emptyLinePlaceholder":51},[32,3332,3333,3335,3337,3339,3342,3345,3348,3351,3354,3356],{"class":34,"line":201},[32,3334,239],{"class":58},[32,3336,3204],{"class":69},[32,3338,261],{"class":58},[32,3340,3341],{"class":540}," 1",[32,3343,3344],{"class":69},"; j ",[32,3346,3347],{"class":58},"\u003C=",[32,3349,3350],{"class":540}," 5",[32,3352,3353],{"class":69},"; j",[32,3355,553],{"class":58},[32,3357,70],{"class":69},[32,3359,3360,3363,3365],{"class":34,"line":221},[32,3361,3362],{"class":69},"        jobs ",[32,3364,334],{"class":58},[32,3366,3223],{"class":69},[32,3368,3369],{"class":34,"line":236},[32,3370,399],{"class":69},[32,3372,3373,3375],{"class":34,"line":244},[32,3374,643],{"class":62},[32,3376,3377],{"class":69},"(jobs)\n",[32,3379,3380],{"class":34,"line":252},[32,3381,52],{"emptyLinePlaceholder":51},[32,3383,3384,3386,3388,3390,3392,3395,3397,3399,3402,3404],{"class":34,"line":270},[32,3385,239],{"class":58},[32,3387,727],{"class":69},[32,3389,261],{"class":58},[32,3391,541],{"class":540},[32,3393,3394],{"class":69},"; r ",[32,3396,547],{"class":58},[32,3398,3350],{"class":540},[32,3400,3401],{"class":69},"; r",[32,3403,553],{"class":58},[32,3405,70],{"class":69},[32,3407,3408,3410,3412,3414,3416],{"class":34,"line":282},[32,3409,1420],{"class":69},[32,3411,1423],{"class":62},[32,3413,153],{"class":69},[32,3415,334],{"class":58},[32,3417,3418],{"class":69},"results)\n",[32,3420,3421],{"class":34,"line":291},[32,3422,399],{"class":69},[32,3424,3425],{"class":34,"line":297},[32,3426,94],{"class":69},[3428,3429,3430],"p",{},"Does this program terminate? What's printed (order may vary)?",[3432,3433,3434,3438,3441,3449,3452,3485],"details",{},[3435,3436,3437],"summary",{},"Answer",[3428,3439,3440],{},"Yes, it terminates. Printed (in some order):",[23,3442,3447],{"className":3443,"code":3445,"language":3446},[3444],"language-text","1\n4\n9\n16\n25\n","text",[29,3448,3445],{"__ignoreMap":27},[3428,3450,3451],{},"The program works because:",[3453,3454,3455,3461,3467,3476,3482],"ol",{},[2987,3456,3457,3458,3460],{},"3 workers start, reading from the shared ",[29,3459,180],{}," channel.",[2987,3462,3463,3464,3466],{},"5 jobs are sent to the buffered ",[29,3465,180],{}," channel (capacity 100 — no blocking).",[2987,3468,3469,3472,3473,3475],{},[29,3470,3471],{},"close(jobs)"," signals no more jobs. Workers' ",[29,3474,3009],{}," loops end when jobs is drained.",[2987,3477,3478,3479,3481],{},"Workers write results to ",[29,3480,190],{}," (buffered, capacity 100 — no blocking).",[2987,3483,3484],{},"Main reads 5 results — all 5 are produced.",[3428,3486,3487,3488,3491,3492,3494,3495,3497],{},"But the program has a subtle issue: it doesn't ",[29,3489,3490],{},"close(results)"," or wait for workers to finish. If we changed the buffer to 0 (unbuffered results), and main read fewer than 5 results, workers would block forever. The ",[29,3493,215],{}," + ",[29,3496,3490],{}," pattern (from the full worker pool example) is more robust.",[14,3499,3501],{"id":3500},"whats-next","📚 What's Next",[3428,3503,3504,3505,3510],{},"→ ",[3506,3507,3509],"a",{"href":3508},"\u002Fgo\u002F27-profiling-and-performance","27 — Profiling & Performance"," — pprof, escape analysis, allocation reduction, and benchmark-driven optimization.",[3512,3513,3514],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}",{"title":27,"searchDepth":42,"depth":42,"links":3516},[3517,3518,3519],{"id":16,"depth":42,"text":17},{"id":771,"depth":42,"text":772},{"id":3500,"depth":42,"text":3501},"Worker pool, pipeline, fan-out\u002Ffan-in, generator, errgroup bounded concurrency, semaphore, and graceful shutdown patterns.","md",{},"\u002Fgo\u002F26-concurrency-patterns",{"title":5,"description":3520},"go\u002F26-concurrency-patterns","XlRkirSOwoQMC8jgrP_-w_wrTO38psSKhkSsyEgnbkg",1789924648784]