[{"data":1,"prerenderedAt":5959},["ShallowReactive",2],{"content:\u002F03-concurrency\u002F01-goroutines-channels":3},{"title":4,"description":5,"path":6,"body":7},"Горутины и каналы","Конкурентность — одна из главных причин, по которой Go стал популярен в backend-разработке. Модель построена на двух примитивах: горутинах как единице исполнения и каналах как способе коммуникации между ними. Понять их в отрыве друг от друга можно, но бессмысленно — они проектировались вместе и работают вместе.","\u002F03-concurrency\u002F01-goroutines-channels",{"type":8,"value":9,"toc":5928},"minimark",[10,15,18,24,27,32,40,237,247,252,255,321,329,404,408,415,521,525,528,617,674,681,787,794,846,848,852,858,908,912,919,1081,1084,1088,1091,1213,1216,1220,1227,1240,1411,1417,1421,1424,1606,1608,1612,1623,1739,1792,1795,1819,1821,1825,1835,1981,1985,1991,2072,2076,2079,2335,2344,2348,2351,2779,2781,2785,2788,2792,2860,2866,2870,2876,3009,3024,3026,3030,3033,3082,3204,3211,3213,3217,3221,3374,3378,3416,3420,3506,3508,3512,3521,3540,3548,3560,3571,3579,3602,3626,3652,3664,3680,3684,3686,3691,3697,3703,3713,3718,3726,3731,4020,4022,4027,4032,4037,4059,4063,4069,4073,4443,4445,4450,4455,4460,4480,4484,4490,4499,4503,5229,5231,5235,5276,5507,5633,5924],[11,12,14],"h1",{"id":13},"горутины-и-каналы-в-go","Горутины и каналы в Go",[16,17,5],"p",{},[19,20,21],"blockquote",{},[16,22,23],{},"\"Do not communicate by sharing memory; instead, share memory by communicating\" — главный принцип конкурентности в Go.",[25,26],"hr",{},[28,29,31],"h2",{"id":30},"горутины","Горутины",[16,33,34,35,39],{},"Горутина — это легковесный поток исполнения, управляемый рантаймом Go, а не операционной системой. Запускается ключевым словом ",[36,37,38],"code",{},"go",":",[41,42,46],"pre",{"className":43,"code":44,"language":38,"meta":45,"style":45},"language-go shiki shiki-themes github-dark","package main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\nfunc main() {\n    go sayHello() \u002F\u002F запуск горутины — не блокирует\n    go func() {   \u002F\u002F анонимная функция\n        fmt.Println(\"anonymous goroutine\")\n    }()\n\n    time.Sleep(time.Second) \u002F\u002F даём горутинам время выполниться\n}\n\nfunc sayHello() {\n    fmt.Println(\"hello from goroutine\")\n}\n","",[36,47,48,61,68,78,91,101,107,112,124,140,154,171,177,182,197,203,208,217,232],{"__ignoreMap":45},[49,50,53,57],"span",{"class":51,"line":52},"line",1,[49,54,56],{"class":55},"snl16","package",[49,58,60],{"class":59},"svObZ"," main\n",[49,62,64],{"class":51,"line":63},2,[49,65,67],{"emptyLinePlaceholder":66},true,"\n",[49,69,71,74],{"class":51,"line":70},3,[49,72,73],{"class":55},"import",[49,75,77],{"class":76},"s95oV"," (\n",[49,79,81,85,88],{"class":51,"line":80},4,[49,82,84],{"class":83},"sU2Wk","    \"",[49,86,87],{"class":59},"fmt",[49,89,90],{"class":83},"\"\n",[49,92,94,96,99],{"class":51,"line":93},5,[49,95,84],{"class":83},[49,97,98],{"class":59},"time",[49,100,90],{"class":83},[49,102,104],{"class":51,"line":103},6,[49,105,106],{"class":76},")\n",[49,108,110],{"class":51,"line":109},7,[49,111,67],{"emptyLinePlaceholder":66},[49,113,115,118,121],{"class":51,"line":114},8,[49,116,117],{"class":55},"func",[49,119,120],{"class":59}," main",[49,122,123],{"class":76},"() {\n",[49,125,127,130,133,136],{"class":51,"line":126},9,[49,128,129],{"class":55},"    go",[49,131,132],{"class":59}," sayHello",[49,134,135],{"class":76},"() ",[49,137,139],{"class":138},"sAwPA","\u002F\u002F запуск горутины — не блокирует\n",[49,141,143,145,148,151],{"class":51,"line":142},10,[49,144,129],{"class":55},[49,146,147],{"class":55}," func",[49,149,150],{"class":76},"() {   ",[49,152,153],{"class":138},"\u002F\u002F анонимная функция\n",[49,155,157,160,163,166,169],{"class":51,"line":156},11,[49,158,159],{"class":76},"        fmt.",[49,161,162],{"class":59},"Println",[49,164,165],{"class":76},"(",[49,167,168],{"class":83},"\"anonymous goroutine\"",[49,170,106],{"class":76},[49,172,174],{"class":51,"line":173},12,[49,175,176],{"class":76},"    }()\n",[49,178,180],{"class":51,"line":179},13,[49,181,67],{"emptyLinePlaceholder":66},[49,183,185,188,191,194],{"class":51,"line":184},14,[49,186,187],{"class":76},"    time.",[49,189,190],{"class":59},"Sleep",[49,192,193],{"class":76},"(time.Second) ",[49,195,196],{"class":138},"\u002F\u002F даём горутинам время выполниться\n",[49,198,200],{"class":51,"line":199},15,[49,201,202],{"class":76},"}\n",[49,204,206],{"class":51,"line":205},16,[49,207,67],{"emptyLinePlaceholder":66},[49,209,211,213,215],{"class":51,"line":210},17,[49,212,117],{"class":55},[49,214,132],{"class":59},[49,216,123],{"class":76},[49,218,220,223,225,227,230],{"class":51,"line":219},18,[49,221,222],{"class":76},"    fmt.",[49,224,162],{"class":59},[49,226,165],{"class":76},[49,228,229],{"class":83},"\"hello from goroutine\"",[49,231,106],{"class":76},[49,233,235],{"class":51,"line":234},19,[49,236,202],{"class":76},[16,238,239,242,243,246],{},[36,240,241],{},"time.Sleep"," здесь — плохой способ ждать горутины. Правильный — каналы или ",[36,244,245],{},"sync.WaitGroup",". К этому вернёмся чуть ниже.",[248,249,251],"h3",{"id":250},"почему-горутины-дёшевы","Почему горутины дёшевы",[16,253,254],{},"Разница между горутиной и OS-потоком принципиальная:",[256,257,258,273],"table",{},[259,260,261],"thead",{},[262,263,264,268,271],"tr",{},[265,266,267],"th",{},"OS Thread",[265,269,270],{},"Горутина",[265,272],{},[274,275,276,288,299,310],"tbody",{},[262,277,278,282,285],{},[279,280,281],"td",{},"Начальный стек",[279,283,284],{},"1–8 МБ (фиксирован)",[279,286,287],{},"2–8 КБ (растёт динамически)",[262,289,290,293,296],{},[279,291,292],{},"Создание",[279,294,295],{},"~1–10 мкс (syscall)",[279,297,298],{},"~0.3 мкс (только рантайм)",[262,300,301,304,307],{},[279,302,303],{},"Переключение",[279,305,306],{},"~1–2 мкс (kernel)",[279,308,309],{},"~0.1 мкс (userspace)",[262,311,312,315,318],{},[279,313,314],{},"Типичное кол-во",[279,316,317],{},"тысячи",[279,319,320],{},"сотни тысяч",[16,322,323,324,328],{},"Стек горутины начинается с 2–8 КБ и ",[325,326,327],"strong",{},"растёт по необходимости"," — рантайм автоматически выделяет больший стек и переносит содержимое. Это позволяет иметь сотни тысяч горутин одновременно без исчерпания памяти.",[41,330,332],{"className":43,"code":331,"language":38,"meta":45,"style":45},"\u002F\u002F Это нормально в Go\nfor i := 0; i \u003C 100_000; i++ {\n    go func(n int) {\n        \u002F\u002F какая-то работа\n    }(i)\n}\n",[36,333,334,339,372,390,395,400],{"__ignoreMap":45},[49,335,336],{"class":51,"line":52},[49,337,338],{"class":138},"\u002F\u002F Это нормально в Go\n",[49,340,341,344,347,350,354,357,360,363,366,369],{"class":51,"line":63},[49,342,343],{"class":55},"for",[49,345,346],{"class":76}," i ",[49,348,349],{"class":55},":=",[49,351,353],{"class":352},"sDLfK"," 0",[49,355,356],{"class":76},"; i ",[49,358,359],{"class":55},"\u003C",[49,361,362],{"class":352}," 100_000",[49,364,365],{"class":76},"; i",[49,367,368],{"class":55},"++",[49,370,371],{"class":76}," {\n",[49,373,374,376,378,380,384,387],{"class":51,"line":70},[49,375,129],{"class":55},[49,377,147],{"class":55},[49,379,165],{"class":76},[49,381,383],{"class":382},"s9osk","n",[49,385,386],{"class":55}," int",[49,388,389],{"class":76},") {\n",[49,391,392],{"class":51,"line":80},[49,393,394],{"class":138},"        \u002F\u002F какая-то работа\n",[49,396,397],{"class":51,"line":93},[49,398,399],{"class":76},"    }(i)\n",[49,401,402],{"class":51,"line":103},[49,403,202],{"class":76},[248,405,407],{"id":406},"жизненный-цикл-горутины","Жизненный цикл горутины",[16,409,410,411,414],{},"Горутина живёт пока не вернётся из функции, которую она запускает. Если ",[36,412,413],{},"main"," завершается — все горутины убиваются немедленно, независимо от их состояния:",[41,416,419],{"className":43,"code":417,"language":38,"meta":418,"style":45},"package main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\nfunc main() {\n    go func() {\n        time.Sleep(10 * time.Second)\n        fmt.Println(\"я никогда не выполнюсь\")\n    }()\n    \u002F\u002F main заканчивается — горутина выше умирает вместе с процессом\n}\n","static",[36,420,421,427,431,437,445,453,457,461,469,477,495,508,512,517],{"__ignoreMap":45},[49,422,423,425],{"class":51,"line":52},[49,424,56],{"class":55},[49,426,60],{"class":59},[49,428,429],{"class":51,"line":63},[49,430,67],{"emptyLinePlaceholder":66},[49,432,433,435],{"class":51,"line":70},[49,434,73],{"class":55},[49,436,77],{"class":76},[49,438,439,441,443],{"class":51,"line":80},[49,440,84],{"class":83},[49,442,87],{"class":59},[49,444,90],{"class":83},[49,446,447,449,451],{"class":51,"line":93},[49,448,84],{"class":83},[49,450,98],{"class":59},[49,452,90],{"class":83},[49,454,455],{"class":51,"line":103},[49,456,106],{"class":76},[49,458,459],{"class":51,"line":109},[49,460,67],{"emptyLinePlaceholder":66},[49,462,463,465,467],{"class":51,"line":114},[49,464,117],{"class":55},[49,466,120],{"class":59},[49,468,123],{"class":76},[49,470,471,473,475],{"class":51,"line":126},[49,472,129],{"class":55},[49,474,147],{"class":55},[49,476,123],{"class":76},[49,478,479,482,484,486,489,492],{"class":51,"line":142},[49,480,481],{"class":76},"        time.",[49,483,190],{"class":59},[49,485,165],{"class":76},[49,487,488],{"class":352},"10",[49,490,491],{"class":55}," *",[49,493,494],{"class":76}," time.Second)\n",[49,496,497,499,501,503,506],{"class":51,"line":156},[49,498,159],{"class":76},[49,500,162],{"class":59},[49,502,165],{"class":76},[49,504,505],{"class":83},"\"я никогда не выполнюсь\"",[49,507,106],{"class":76},[49,509,510],{"class":51,"line":173},[49,511,176],{"class":76},[49,513,514],{"class":51,"line":179},[49,515,516],{"class":138},"    \u002F\u002F main заканчивается — горутина выше умирает вместе с процессом\n",[49,518,519],{"class":51,"line":184},[49,520,202],{"class":76},[248,522,524],{"id":523},"утечки-горутин","Утечки горутин",[16,526,527],{},"Утечка горутины — это когда горутина запущена, но никогда не завершается. Она держит память, стек и все захваченные переменные:",[41,529,531],{"className":43,"code":530,"language":38,"meta":418,"style":45},"\u002F\u002F Классическая утечка: горутина заблокирована на чтении из канала\n\u002F\u002F который никто никогда не закроет и в который никто не пишет\nfunc leak() {\n    ch := make(chan int)\n    go func() {\n        val := \u003C-ch \u002F\u002F блокируется навсегда\n        fmt.Println(val)\n    }()\n    \u002F\u002F ch выходит из scope, но горутина жива\n}\n",[36,532,533,538,543,552,571,579,595,604,608,613],{"__ignoreMap":45},[49,534,535],{"class":51,"line":52},[49,536,537],{"class":138},"\u002F\u002F Классическая утечка: горутина заблокирована на чтении из канала\n",[49,539,540],{"class":51,"line":63},[49,541,542],{"class":138},"\u002F\u002F который никто никогда не закроет и в который никто не пишет\n",[49,544,545,547,550],{"class":51,"line":70},[49,546,117],{"class":55},[49,548,549],{"class":59}," leak",[49,551,123],{"class":76},[49,553,554,557,559,562,564,567,569],{"class":51,"line":80},[49,555,556],{"class":76},"    ch ",[49,558,349],{"class":55},[49,560,561],{"class":59}," make",[49,563,165],{"class":76},[49,565,566],{"class":55},"chan",[49,568,386],{"class":55},[49,570,106],{"class":76},[49,572,573,575,577],{"class":51,"line":93},[49,574,129],{"class":55},[49,576,147],{"class":55},[49,578,123],{"class":76},[49,580,581,584,586,589,592],{"class":51,"line":103},[49,582,583],{"class":76},"        val ",[49,585,349],{"class":55},[49,587,588],{"class":55}," \u003C-",[49,590,591],{"class":76},"ch ",[49,593,594],{"class":138},"\u002F\u002F блокируется навсегда\n",[49,596,597,599,601],{"class":51,"line":109},[49,598,159],{"class":76},[49,600,162],{"class":59},[49,602,603],{"class":76},"(val)\n",[49,605,606],{"class":51,"line":114},[49,607,176],{"class":76},[49,609,610],{"class":51,"line":126},[49,611,612],{"class":138},"    \u002F\u002F ch выходит из scope, но горутина жива\n",[49,614,615],{"class":51,"line":142},[49,616,202],{"class":76},[41,618,620],{"className":43,"code":619,"language":38,"meta":45,"style":45},"\u002F\u002F Утечка через бесконечный цикл без условия выхода\nfunc leak2() {\n    go func() {\n        for {\n            doWork() \u002F\u002F нет способа остановить эту горутину снаружи\n        }\n    }()\n}\n",[36,621,622,627,636,644,651,661,666,670],{"__ignoreMap":45},[49,623,624],{"class":51,"line":52},[49,625,626],{"class":138},"\u002F\u002F Утечка через бесконечный цикл без условия выхода\n",[49,628,629,631,634],{"class":51,"line":63},[49,630,117],{"class":55},[49,632,633],{"class":59}," leak2",[49,635,123],{"class":76},[49,637,638,640,642],{"class":51,"line":70},[49,639,129],{"class":55},[49,641,147],{"class":55},[49,643,123],{"class":76},[49,645,646,649],{"class":51,"line":80},[49,647,648],{"class":55},"        for",[49,650,371],{"class":76},[49,652,653,656,658],{"class":51,"line":93},[49,654,655],{"class":59},"            doWork",[49,657,135],{"class":76},[49,659,660],{"class":138},"\u002F\u002F нет способа остановить эту горутину снаружи\n",[49,662,663],{"class":51,"line":103},[49,664,665],{"class":76},"        }\n",[49,667,668],{"class":51,"line":109},[49,669,176],{"class":76},[49,671,672],{"class":51,"line":114},[49,673,202],{"class":76},[16,675,676,677,680],{},"Правильный способ — всегда давать горутине способ завершиться. Обычно это ",[36,678,679],{},"context.Context"," или канал-сигнал:",[41,682,684],{"className":43,"code":683,"language":38,"meta":45,"style":45},"func noLeak(ctx context.Context) {\n    go func() {\n        for {\n            select {\n            case \u003C-ctx.Done(): \u002F\u002F получили сигнал завершения\n                return\n            default:\n                doWork()\n            }\n        }\n    }()\n}\n",[36,685,686,709,717,723,730,749,754,762,770,775,779,783],{"__ignoreMap":45},[49,687,688,690,693,695,698,701,704,707],{"class":51,"line":52},[49,689,117],{"class":55},[49,691,692],{"class":59}," noLeak",[49,694,165],{"class":76},[49,696,697],{"class":382},"ctx",[49,699,700],{"class":59}," context",[49,702,703],{"class":76},".",[49,705,706],{"class":59},"Context",[49,708,389],{"class":76},[49,710,711,713,715],{"class":51,"line":63},[49,712,129],{"class":55},[49,714,147],{"class":55},[49,716,123],{"class":76},[49,718,719,721],{"class":51,"line":70},[49,720,648],{"class":55},[49,722,371],{"class":76},[49,724,725,728],{"class":51,"line":80},[49,726,727],{"class":55},"            select",[49,729,371],{"class":76},[49,731,732,735,737,740,743,746],{"class":51,"line":93},[49,733,734],{"class":55},"            case",[49,736,588],{"class":55},[49,738,739],{"class":76},"ctx.",[49,741,742],{"class":59},"Done",[49,744,745],{"class":76},"(): ",[49,747,748],{"class":138},"\u002F\u002F получили сигнал завершения\n",[49,750,751],{"class":51,"line":103},[49,752,753],{"class":55},"                return\n",[49,755,756,759],{"class":51,"line":109},[49,757,758],{"class":55},"            default",[49,760,761],{"class":76},":\n",[49,763,764,767],{"class":51,"line":114},[49,765,766],{"class":59},"                doWork",[49,768,769],{"class":76},"()\n",[49,771,772],{"class":51,"line":126},[49,773,774],{"class":76},"            }\n",[49,776,777],{"class":51,"line":142},[49,778,665],{"class":76},[49,780,781],{"class":51,"line":156},[49,782,176],{"class":76},[49,784,785],{"class":51,"line":173},[49,786,202],{"class":76},[16,788,789,790,793],{},"Обнаружить утечки помогает ",[36,791,792],{},"goleak"," от Uber — библиотека для тестов, которая проверяет что после теста не осталось лишних горутин:",[41,795,797],{"className":43,"code":796,"language":38,"meta":45,"style":45},"func TestMyFunc(t *testing.T) {\n    defer goleak.VerifyNone(t)\n    \u002F\u002F тест\n}\n",[36,798,799,823,837,842],{"__ignoreMap":45},[49,800,801,803,806,808,811,813,816,818,821],{"class":51,"line":52},[49,802,117],{"class":55},[49,804,805],{"class":59}," TestMyFunc",[49,807,165],{"class":76},[49,809,810],{"class":382},"t",[49,812,491],{"class":55},[49,814,815],{"class":59},"testing",[49,817,703],{"class":76},[49,819,820],{"class":59},"T",[49,822,389],{"class":76},[49,824,825,828,831,834],{"class":51,"line":63},[49,826,827],{"class":55},"    defer",[49,829,830],{"class":76}," goleak.",[49,832,833],{"class":59},"VerifyNone",[49,835,836],{"class":76},"(t)\n",[49,838,839],{"class":51,"line":70},[49,840,841],{"class":138},"    \u002F\u002F тест\n",[49,843,844],{"class":51,"line":80},[49,845,202],{"class":76},[25,847],{},[28,849,851],{"id":850},"каналы","Каналы",[16,853,854,855,39],{},"Канал — это типизированный, потокобезопасный способ передавать данные между горутинами. Создаётся через ",[36,856,857],{},"make",[41,859,861],{"className":43,"code":860,"language":38,"meta":45,"style":45},"ch := make(chan int)        \u002F\u002F небуферизованный\nch := make(chan int, 10)    \u002F\u002F буферизованный на 10 элементов\n",[36,862,863,883],{"__ignoreMap":45},[49,864,865,867,869,871,873,875,877,880],{"class":51,"line":52},[49,866,591],{"class":76},[49,868,349],{"class":55},[49,870,561],{"class":59},[49,872,165],{"class":76},[49,874,566],{"class":55},[49,876,386],{"class":55},[49,878,879],{"class":76},")        ",[49,881,882],{"class":138},"\u002F\u002F небуферизованный\n",[49,884,885,887,889,891,893,895,897,900,902,905],{"class":51,"line":63},[49,886,591],{"class":76},[49,888,349],{"class":55},[49,890,561],{"class":59},[49,892,165],{"class":76},[49,894,566],{"class":55},[49,896,386],{"class":55},[49,898,899],{"class":76},", ",[49,901,488],{"class":352},[49,903,904],{"class":76},")    ",[49,906,907],{"class":138},"\u002F\u002F буферизованный на 10 элементов\n",[248,909,911],{"id":910},"небуферизованный-канал-рандеву","Небуферизованный канал — рандеву",[16,913,914,915,918],{},"Небуферизованный канал синхронизирует отправителя и получателя: ",[325,916,917],{},"отправка блокируется до тех пор, пока получатель не готов принять",", и наоборот:",[41,920,922],{"className":43,"code":921,"language":38,"meta":45,"style":45},"ch := make(chan int)\n\ngo func() {\n    fmt.Println(\"отправляю...\")\n    ch \u003C- 42 \u002F\u002F блокируется пока main не прочитает\n    fmt.Println(\"отправил\")\n}()\n\ntime.Sleep(time.Second)\nfmt.Println(\"получаю...\")\nval := \u003C-ch \u002F\u002F разблокирует горутину выше\nfmt.Println(\"получил:\", val)\n\n\u002F\u002F Вывод:\n\u002F\u002F отправляю...\n\u002F\u002F получаю...\n\u002F\u002F отправил\n\u002F\u002F получил: 42\n",[36,923,924,940,944,952,965,978,991,996,1000,1010,1024,1038,1052,1056,1061,1066,1071,1076],{"__ignoreMap":45},[49,925,926,928,930,932,934,936,938],{"class":51,"line":52},[49,927,591],{"class":76},[49,929,349],{"class":55},[49,931,561],{"class":59},[49,933,165],{"class":76},[49,935,566],{"class":55},[49,937,386],{"class":55},[49,939,106],{"class":76},[49,941,942],{"class":51,"line":63},[49,943,67],{"emptyLinePlaceholder":66},[49,945,946,948,950],{"class":51,"line":70},[49,947,38],{"class":55},[49,949,147],{"class":55},[49,951,123],{"class":76},[49,953,954,956,958,960,963],{"class":51,"line":80},[49,955,222],{"class":76},[49,957,162],{"class":59},[49,959,165],{"class":76},[49,961,962],{"class":83},"\"отправляю...\"",[49,964,106],{"class":76},[49,966,967,969,972,975],{"class":51,"line":93},[49,968,556],{"class":76},[49,970,971],{"class":55},"\u003C-",[49,973,974],{"class":352}," 42",[49,976,977],{"class":138}," \u002F\u002F блокируется пока main не прочитает\n",[49,979,980,982,984,986,989],{"class":51,"line":103},[49,981,222],{"class":76},[49,983,162],{"class":59},[49,985,165],{"class":76},[49,987,988],{"class":83},"\"отправил\"",[49,990,106],{"class":76},[49,992,993],{"class":51,"line":109},[49,994,995],{"class":76},"}()\n",[49,997,998],{"class":51,"line":114},[49,999,67],{"emptyLinePlaceholder":66},[49,1001,1002,1005,1007],{"class":51,"line":126},[49,1003,1004],{"class":76},"time.",[49,1006,190],{"class":59},[49,1008,1009],{"class":76},"(time.Second)\n",[49,1011,1012,1015,1017,1019,1022],{"class":51,"line":142},[49,1013,1014],{"class":76},"fmt.",[49,1016,162],{"class":59},[49,1018,165],{"class":76},[49,1020,1021],{"class":83},"\"получаю...\"",[49,1023,106],{"class":76},[49,1025,1026,1029,1031,1033,1035],{"class":51,"line":156},[49,1027,1028],{"class":76},"val ",[49,1030,349],{"class":55},[49,1032,588],{"class":55},[49,1034,591],{"class":76},[49,1036,1037],{"class":138},"\u002F\u002F разблокирует горутину выше\n",[49,1039,1040,1042,1044,1046,1049],{"class":51,"line":173},[49,1041,1014],{"class":76},[49,1043,162],{"class":59},[49,1045,165],{"class":76},[49,1047,1048],{"class":83},"\"получил:\"",[49,1050,1051],{"class":76},", val)\n",[49,1053,1054],{"class":51,"line":179},[49,1055,67],{"emptyLinePlaceholder":66},[49,1057,1058],{"class":51,"line":184},[49,1059,1060],{"class":138},"\u002F\u002F Вывод:\n",[49,1062,1063],{"class":51,"line":199},[49,1064,1065],{"class":138},"\u002F\u002F отправляю...\n",[49,1067,1068],{"class":51,"line":205},[49,1069,1070],{"class":138},"\u002F\u002F получаю...\n",[49,1072,1073],{"class":51,"line":210},[49,1074,1075],{"class":138},"\u002F\u002F отправил\n",[49,1077,1078],{"class":51,"line":219},[49,1079,1080],{"class":138},"\u002F\u002F получил: 42\n",[16,1082,1083],{},"Это называется рандеву (rendezvous) — встреча двух горутин в точке передачи данных. Именно поэтому небуферизованные каналы — мощный инструмент синхронизации.",[248,1085,1087],{"id":1086},"буферизованный-канал-асинхронная-очередь","Буферизованный канал — асинхронная очередь",[16,1089,1090],{},"Буферизованный канал позволяет отправить до N элементов не блокируясь, пока буфер не заполнен:",[41,1092,1094],{"className":43,"code":1093,"language":38,"meta":45,"style":45},"ch := make(chan int, 3)\n\nch \u003C- 1 \u002F\u002F не блокируется\nch \u003C- 2 \u002F\u002F не блокируется\nch \u003C- 3 \u002F\u002F не блокируется\n\u002F\u002F ch \u003C- 4 \u002F\u002F заблокировалось бы — буфер полон\n\nfmt.Println(\u003C-ch) \u002F\u002F 1\nfmt.Println(\u003C-ch) \u002F\u002F 2\nfmt.Println(\u003C-ch) \u002F\u002F 3\n",[36,1095,1096,1117,1121,1133,1144,1155,1163,1167,1183,1198],{"__ignoreMap":45},[49,1097,1098,1100,1102,1104,1106,1108,1110,1112,1115],{"class":51,"line":52},[49,1099,591],{"class":76},[49,1101,349],{"class":55},[49,1103,561],{"class":59},[49,1105,165],{"class":76},[49,1107,566],{"class":55},[49,1109,386],{"class":55},[49,1111,899],{"class":76},[49,1113,1114],{"class":352},"3",[49,1116,106],{"class":76},[49,1118,1119],{"class":51,"line":63},[49,1120,67],{"emptyLinePlaceholder":66},[49,1122,1123,1125,1127,1130],{"class":51,"line":70},[49,1124,591],{"class":76},[49,1126,971],{"class":55},[49,1128,1129],{"class":352}," 1",[49,1131,1132],{"class":138}," \u002F\u002F не блокируется\n",[49,1134,1135,1137,1139,1142],{"class":51,"line":80},[49,1136,591],{"class":76},[49,1138,971],{"class":55},[49,1140,1141],{"class":352}," 2",[49,1143,1132],{"class":138},[49,1145,1146,1148,1150,1153],{"class":51,"line":93},[49,1147,591],{"class":76},[49,1149,971],{"class":55},[49,1151,1152],{"class":352}," 3",[49,1154,1132],{"class":138},[49,1156,1157,1160],{"class":51,"line":103},[49,1158,1159],{"class":138},"\u002F\u002F ch \u003C- 4",[49,1161,1162],{"class":138}," \u002F\u002F заблокировалось бы — буфер полон\n",[49,1164,1165],{"class":51,"line":109},[49,1166,67],{"emptyLinePlaceholder":66},[49,1168,1169,1171,1173,1175,1177,1180],{"class":51,"line":114},[49,1170,1014],{"class":76},[49,1172,162],{"class":59},[49,1174,165],{"class":76},[49,1176,971],{"class":55},[49,1178,1179],{"class":76},"ch) ",[49,1181,1182],{"class":138},"\u002F\u002F 1\n",[49,1184,1185,1187,1189,1191,1193,1195],{"class":51,"line":126},[49,1186,1014],{"class":76},[49,1188,162],{"class":59},[49,1190,165],{"class":76},[49,1192,971],{"class":55},[49,1194,1179],{"class":76},[49,1196,1197],{"class":138},"\u002F\u002F 2\n",[49,1199,1200,1202,1204,1206,1208,1210],{"class":51,"line":142},[49,1201,1014],{"class":76},[49,1203,162],{"class":59},[49,1205,165],{"class":76},[49,1207,971],{"class":55},[49,1209,1179],{"class":76},[49,1211,1212],{"class":138},"\u002F\u002F 3\n",[16,1214,1215],{},"Буферизованный канал ведёт себя как очередь FIFO. Полезен когда производитель и потребитель работают с разной скоростью и нужно сгладить пики.",[248,1217,1219],{"id":1218},"закрытие-канала","Закрытие канала",[16,1221,1222,1223,1226],{},"Канал закрывается через ",[36,1224,1225],{},"close",". Закрытый канал:",[1228,1229,1230,1234,1237],"ul",{},[1231,1232,1233],"li",{},"Возвращает zero value немедленно при чтении",[1231,1235,1236],{},"Паникует при записи",[1231,1238,1239],{},"Паникует при повторном закрытии",[41,1241,1243],{"className":43,"code":1242,"language":38,"meta":45,"style":45},"ch := make(chan int, 3)\nch \u003C- 1\nch \u003C- 2\nclose(ch)\n\n\u002F\u002F Чтение из закрытого канала\nv, ok := \u003C-ch\nfmt.Println(v, ok) \u002F\u002F 1 true — данные ещё есть\n\nv, ok = \u003C-ch\nfmt.Println(v, ok) \u002F\u002F 2 true\n\nv, ok = \u003C-ch\nfmt.Println(v, ok) \u002F\u002F 0 false — канал закрыт и пуст\n\n\u002F\u002F range по каналу — завершится когда канал закрыт и пуст\nfor v := range ch {\n    fmt.Println(v)\n}\n",[36,1244,1245,1265,1274,1283,1290,1294,1299,1311,1323,1327,1338,1349,1353,1363,1374,1378,1383,1398,1407],{"__ignoreMap":45},[49,1246,1247,1249,1251,1253,1255,1257,1259,1261,1263],{"class":51,"line":52},[49,1248,591],{"class":76},[49,1250,349],{"class":55},[49,1252,561],{"class":59},[49,1254,165],{"class":76},[49,1256,566],{"class":55},[49,1258,386],{"class":55},[49,1260,899],{"class":76},[49,1262,1114],{"class":352},[49,1264,106],{"class":76},[49,1266,1267,1269,1271],{"class":51,"line":63},[49,1268,591],{"class":76},[49,1270,971],{"class":55},[49,1272,1273],{"class":352}," 1\n",[49,1275,1276,1278,1280],{"class":51,"line":70},[49,1277,591],{"class":76},[49,1279,971],{"class":55},[49,1281,1282],{"class":352}," 2\n",[49,1284,1285,1287],{"class":51,"line":80},[49,1286,1225],{"class":59},[49,1288,1289],{"class":76},"(ch)\n",[49,1291,1292],{"class":51,"line":93},[49,1293,67],{"emptyLinePlaceholder":66},[49,1295,1296],{"class":51,"line":103},[49,1297,1298],{"class":138},"\u002F\u002F Чтение из закрытого канала\n",[49,1300,1301,1304,1306,1308],{"class":51,"line":109},[49,1302,1303],{"class":76},"v, ok ",[49,1305,349],{"class":55},[49,1307,588],{"class":55},[49,1309,1310],{"class":76},"ch\n",[49,1312,1313,1315,1317,1320],{"class":51,"line":114},[49,1314,1014],{"class":76},[49,1316,162],{"class":59},[49,1318,1319],{"class":76},"(v, ok) ",[49,1321,1322],{"class":138},"\u002F\u002F 1 true — данные ещё есть\n",[49,1324,1325],{"class":51,"line":126},[49,1326,67],{"emptyLinePlaceholder":66},[49,1328,1329,1331,1334,1336],{"class":51,"line":142},[49,1330,1303],{"class":76},[49,1332,1333],{"class":55},"=",[49,1335,588],{"class":55},[49,1337,1310],{"class":76},[49,1339,1340,1342,1344,1346],{"class":51,"line":156},[49,1341,1014],{"class":76},[49,1343,162],{"class":59},[49,1345,1319],{"class":76},[49,1347,1348],{"class":138},"\u002F\u002F 2 true\n",[49,1350,1351],{"class":51,"line":173},[49,1352,67],{"emptyLinePlaceholder":66},[49,1354,1355,1357,1359,1361],{"class":51,"line":179},[49,1356,1303],{"class":76},[49,1358,1333],{"class":55},[49,1360,588],{"class":55},[49,1362,1310],{"class":76},[49,1364,1365,1367,1369,1371],{"class":51,"line":184},[49,1366,1014],{"class":76},[49,1368,162],{"class":59},[49,1370,1319],{"class":76},[49,1372,1373],{"class":138},"\u002F\u002F 0 false — канал закрыт и пуст\n",[49,1375,1376],{"class":51,"line":199},[49,1377,67],{"emptyLinePlaceholder":66},[49,1379,1380],{"class":51,"line":205},[49,1381,1382],{"class":138},"\u002F\u002F range по каналу — завершится когда канал закрыт и пуст\n",[49,1384,1385,1387,1390,1392,1395],{"class":51,"line":210},[49,1386,343],{"class":55},[49,1388,1389],{"class":76}," v ",[49,1391,349],{"class":55},[49,1393,1394],{"class":55}," range",[49,1396,1397],{"class":76}," ch {\n",[49,1399,1400,1402,1404],{"class":51,"line":219},[49,1401,222],{"class":76},[49,1403,162],{"class":59},[49,1405,1406],{"class":76},"(v)\n",[49,1408,1409],{"class":51,"line":234},[49,1410,202],{"class":76},[16,1412,1413,1416],{},[325,1414,1415],{},"Ключевое правило:"," закрывает канал тот, кто пишет — никогда читатель. Иначе запись в закрытый канал вызовет панику.",[248,1418,1420],{"id":1419},"направленные-каналы","Направленные каналы",[16,1422,1423],{},"Тип канала можно ограничить до send-only или receive-only. Это делает намерения явными и ловит ошибки на этапе компиляции:",[41,1425,1427],{"className":43,"code":1426,"language":38,"meta":45,"style":45},"package main\n\nimport \"fmt\"\n\nfunc producer(ch chan\u003C- int) { \u002F\u002F только запись\n    ch \u003C- 42\n    \u002F\u002F \u003C-ch \u002F\u002F Ошибка компиляции: receive from send-only channel\n}\n\nfunc consumer(ch \u003C-chan int) { \u002F\u002F только чтение\n    val := \u003C-ch\n    \u002F\u002F ch \u003C- 1 \u002F\u002F Ошибка компиляции: send to receive-only channel\n    fmt.Println(val)\n}\n\nfunc main() {\n    ch := make(chan int)\n    go producer(ch) \u002F\u002F двунаправленный неявно конвертируется\n    consumer(ch)\n}\n",[36,1428,1429,1435,1439,1450,1454,1477,1486,1494,1498,1502,1523,1534,1542,1550,1554,1558,1566,1582,1594,1601],{"__ignoreMap":45},[49,1430,1431,1433],{"class":51,"line":52},[49,1432,56],{"class":55},[49,1434,60],{"class":59},[49,1436,1437],{"class":51,"line":63},[49,1438,67],{"emptyLinePlaceholder":66},[49,1440,1441,1443,1446,1448],{"class":51,"line":70},[49,1442,73],{"class":55},[49,1444,1445],{"class":83}," \"",[49,1447,87],{"class":59},[49,1449,90],{"class":83},[49,1451,1452],{"class":51,"line":80},[49,1453,67],{"emptyLinePlaceholder":66},[49,1455,1456,1458,1461,1463,1466,1469,1471,1474],{"class":51,"line":93},[49,1457,117],{"class":55},[49,1459,1460],{"class":59}," producer",[49,1462,165],{"class":76},[49,1464,1465],{"class":382},"ch",[49,1467,1468],{"class":55}," chan\u003C-",[49,1470,386],{"class":55},[49,1472,1473],{"class":76},") { ",[49,1475,1476],{"class":138},"\u002F\u002F только запись\n",[49,1478,1479,1481,1483],{"class":51,"line":103},[49,1480,556],{"class":76},[49,1482,971],{"class":55},[49,1484,1485],{"class":352}," 42\n",[49,1487,1488,1491],{"class":51,"line":109},[49,1489,1490],{"class":138},"    \u002F\u002F \u003C-ch",[49,1492,1493],{"class":138}," \u002F\u002F Ошибка компиляции: receive from send-only channel\n",[49,1495,1496],{"class":51,"line":114},[49,1497,202],{"class":76},[49,1499,1500],{"class":51,"line":126},[49,1501,67],{"emptyLinePlaceholder":66},[49,1503,1504,1506,1509,1511,1513,1516,1518,1520],{"class":51,"line":142},[49,1505,117],{"class":55},[49,1507,1508],{"class":59}," consumer",[49,1510,165],{"class":76},[49,1512,1465],{"class":382},[49,1514,1515],{"class":55}," \u003C-chan",[49,1517,386],{"class":55},[49,1519,1473],{"class":76},[49,1521,1522],{"class":138},"\u002F\u002F только чтение\n",[49,1524,1525,1528,1530,1532],{"class":51,"line":156},[49,1526,1527],{"class":76},"    val ",[49,1529,349],{"class":55},[49,1531,588],{"class":55},[49,1533,1310],{"class":76},[49,1535,1536,1539],{"class":51,"line":173},[49,1537,1538],{"class":138},"    \u002F\u002F ch \u003C- 1",[49,1540,1541],{"class":138}," \u002F\u002F Ошибка компиляции: send to receive-only channel\n",[49,1543,1544,1546,1548],{"class":51,"line":179},[49,1545,222],{"class":76},[49,1547,162],{"class":59},[49,1549,603],{"class":76},[49,1551,1552],{"class":51,"line":184},[49,1553,202],{"class":76},[49,1555,1556],{"class":51,"line":199},[49,1557,67],{"emptyLinePlaceholder":66},[49,1559,1560,1562,1564],{"class":51,"line":205},[49,1561,117],{"class":55},[49,1563,120],{"class":59},[49,1565,123],{"class":76},[49,1567,1568,1570,1572,1574,1576,1578,1580],{"class":51,"line":210},[49,1569,556],{"class":76},[49,1571,349],{"class":55},[49,1573,561],{"class":59},[49,1575,165],{"class":76},[49,1577,566],{"class":55},[49,1579,386],{"class":55},[49,1581,106],{"class":76},[49,1583,1584,1586,1588,1591],{"class":51,"line":219},[49,1585,129],{"class":55},[49,1587,1460],{"class":59},[49,1589,1590],{"class":76},"(ch) ",[49,1592,1593],{"class":138},"\u002F\u002F двунаправленный неявно конвертируется\n",[49,1595,1596,1599],{"class":51,"line":234},[49,1597,1598],{"class":59},"    consumer",[49,1600,1289],{"class":76},[49,1602,1604],{"class":51,"line":1603},20,[49,1605,202],{"class":76},[25,1607],{},[28,1609,1611],{"id":1610},"под-капотом-как-устроен-канал","Под капотом: как устроен канал",[16,1613,1614,1615,1618,1619,1622],{},"Канал — не магия компилятора, а обычная структура ",[36,1616,1617],{},"hchan"," в куче. ",[36,1620,1621],{},"make(chan T, size)"," аллоцирует её и возвращает указатель — поэтому канал «передаётся по ссылке» и копия переменной-канала ссылается на тот же буфер. Упрощённо:",[41,1624,1626],{"className":43,"code":1625,"language":38,"meta":418,"style":45},"type hchan struct {\n    qcount   uint           \u002F\u002F сколько элементов в буфере сейчас\n    dataqsiz uint           \u002F\u002F вместимость буфера (size из make)\n    buf      unsafe.Pointer \u002F\u002F кольцевой буфер (только для буферизованных)\n    sendx    uint           \u002F\u002F индекс следующей записи в buf\n    recvx    uint           \u002F\u002F индекс следующего чтения из buf\n    recvq    waitq          \u002F\u002F очередь заблокированных получателей\n    sendq    waitq          \u002F\u002F очередь заблокированных отправителей\n    lock     mutex          \u002F\u002F защищает все поля при любой операции\n    \u002F\u002F ... elemtype, closed и др.\n}\n",[36,1627,1628,1641,1652,1662,1678,1688,1698,1709,1719,1730,1735],{"__ignoreMap":45},[49,1629,1630,1633,1636,1639],{"class":51,"line":52},[49,1631,1632],{"class":55},"type",[49,1634,1635],{"class":59}," hchan",[49,1637,1638],{"class":55}," struct",[49,1640,371],{"class":76},[49,1642,1643,1646,1649],{"class":51,"line":63},[49,1644,1645],{"class":76},"    qcount   ",[49,1647,1648],{"class":55},"uint",[49,1650,1651],{"class":138},"           \u002F\u002F сколько элементов в буфере сейчас\n",[49,1653,1654,1657,1659],{"class":51,"line":70},[49,1655,1656],{"class":76},"    dataqsiz ",[49,1658,1648],{"class":55},[49,1660,1661],{"class":138},"           \u002F\u002F вместимость буфера (size из make)\n",[49,1663,1664,1667,1670,1672,1675],{"class":51,"line":80},[49,1665,1666],{"class":76},"    buf      ",[49,1668,1669],{"class":59},"unsafe",[49,1671,703],{"class":76},[49,1673,1674],{"class":59},"Pointer",[49,1676,1677],{"class":138}," \u002F\u002F кольцевой буфер (только для буферизованных)\n",[49,1679,1680,1683,1685],{"class":51,"line":93},[49,1681,1682],{"class":76},"    sendx    ",[49,1684,1648],{"class":55},[49,1686,1687],{"class":138},"           \u002F\u002F индекс следующей записи в buf\n",[49,1689,1690,1693,1695],{"class":51,"line":103},[49,1691,1692],{"class":76},"    recvx    ",[49,1694,1648],{"class":55},[49,1696,1697],{"class":138},"           \u002F\u002F индекс следующего чтения из buf\n",[49,1699,1700,1703,1706],{"class":51,"line":109},[49,1701,1702],{"class":76},"    recvq    ",[49,1704,1705],{"class":59},"waitq",[49,1707,1708],{"class":138},"          \u002F\u002F очередь заблокированных получателей\n",[49,1710,1711,1714,1716],{"class":51,"line":114},[49,1712,1713],{"class":76},"    sendq    ",[49,1715,1705],{"class":59},[49,1717,1718],{"class":138},"          \u002F\u002F очередь заблокированных отправителей\n",[49,1720,1721,1724,1727],{"class":51,"line":126},[49,1722,1723],{"class":76},"    lock     ",[49,1725,1726],{"class":59},"mutex",[49,1728,1729],{"class":138},"          \u002F\u002F защищает все поля при любой операции\n",[49,1731,1732],{"class":51,"line":142},[49,1733,1734],{"class":138},"    \u002F\u002F ... elemtype, closed и др.\n",[49,1736,1737],{"class":51,"line":156},[49,1738,202],{"class":76},[1228,1740,1741,1770,1776,1786],{},[1231,1742,1743,1746,1747,1750,1751,1754,1755,1758,1759,1762,1763,1766,1767,703],{},[325,1744,1745],{},"buf, qcount, dataqsiz"," — буфер. ",[36,1748,1749],{},"buf"," работает как кольцо (ring buffer): дойдя до конца массива, индексы ",[36,1752,1753],{},"sendx","\u002F",[36,1756,1757],{},"recvx"," оборачиваются в начало, поэтому элементы не сдвигаются. ",[36,1760,1761],{},"qcount"," — сколько лежит сейчас, ",[36,1764,1765],{},"dataqsiz"," — сколько влезает максимум. У небуферизованного канала ",[36,1768,1769],{},"dataqsiz == 0",[1231,1771,1772,1775],{},[325,1773,1774],{},"sendx \u002F recvx"," — куда писать и откуда читать следующий элемент.",[1231,1777,1778,1781,1782,1785],{},[325,1779,1780],{},"sendq \u002F recvq"," — двусвязные списки заблокированных горутин. Если горутина блокируется на отправке или получении, она оборачивается в структуру ",[36,1783,1784],{},"sudog"," и кладётся в соответствующую очередь; при встречной операции рантайм будит её напрямую и копирует значение, минуя буфер.",[1231,1787,1788,1791],{},[325,1789,1790],{},"lock"," — мьютекс, захватывается на любой операции с каналом. То есть канал — это потокобезопасная очередь с буфером и двумя очередями ожидания.",[16,1793,1794],{},"Отсюда несколько следствий, которые любят спрашивать:",[1228,1796,1797,1807,1812],{},[1231,1798,1799,1800,1754,1803,1806],{},"небуферизованный канал всегда передаёт данные «из рук в руки» через ",[36,1801,1802],{},"sendq",[36,1804,1805],{},"recvq",", буфер при этом не используется;",[1231,1808,1809,1811],{},[36,1810,1225],{}," будит все горутины сразу в обеих очередях;",[1231,1813,1814,1815,1818],{},"внутри канала живёт обычный мьютекс, так что канал — не «бесплатная» альтернатива ",[36,1816,1817],{},"sync.Mutex",", а синхронизация поверх него.",[25,1820],{},[28,1822,1824],{"id":1823},"select-мультиплексирование-каналов","select — мультиплексирование каналов",[16,1826,1827,1830,1831,1834],{},[36,1828,1829],{},"select"," — это ",[36,1832,1833],{},"switch"," для каналов. Он ждёт пока хотя бы один из кейсов станет готов, и выполняет его. Если несколько готовы одновременно — выбирает случайный:",[41,1836,1838],{"className":43,"code":1837,"language":38,"meta":45,"style":45},"ch1 := make(chan string)\nch2 := make(chan string)\n\ngo func() { ch1 \u003C- \"один\" }()\ngo func() { ch2 \u003C- \"два\" }()\n\nselect {\ncase msg := \u003C-ch1:\n    fmt.Println(\"из ch1:\", msg)\ncase msg := \u003C-ch2:\n    fmt.Println(\"из ch2:\", msg)\n}\n",[36,1839,1840,1858,1875,1879,1896,1912,1916,1922,1937,1951,1964,1977],{"__ignoreMap":45},[49,1841,1842,1845,1847,1849,1851,1853,1856],{"class":51,"line":52},[49,1843,1844],{"class":76},"ch1 ",[49,1846,349],{"class":55},[49,1848,561],{"class":59},[49,1850,165],{"class":76},[49,1852,566],{"class":55},[49,1854,1855],{"class":55}," string",[49,1857,106],{"class":76},[49,1859,1860,1863,1865,1867,1869,1871,1873],{"class":51,"line":63},[49,1861,1862],{"class":76},"ch2 ",[49,1864,349],{"class":55},[49,1866,561],{"class":59},[49,1868,165],{"class":76},[49,1870,566],{"class":55},[49,1872,1855],{"class":55},[49,1874,106],{"class":76},[49,1876,1877],{"class":51,"line":70},[49,1878,67],{"emptyLinePlaceholder":66},[49,1880,1881,1883,1885,1888,1890,1893],{"class":51,"line":80},[49,1882,38],{"class":55},[49,1884,147],{"class":55},[49,1886,1887],{"class":76},"() { ch1 ",[49,1889,971],{"class":55},[49,1891,1892],{"class":83}," \"один\"",[49,1894,1895],{"class":76}," }()\n",[49,1897,1898,1900,1902,1905,1907,1910],{"class":51,"line":93},[49,1899,38],{"class":55},[49,1901,147],{"class":55},[49,1903,1904],{"class":76},"() { ch2 ",[49,1906,971],{"class":55},[49,1908,1909],{"class":83}," \"два\"",[49,1911,1895],{"class":76},[49,1913,1914],{"class":51,"line":103},[49,1915,67],{"emptyLinePlaceholder":66},[49,1917,1918,1920],{"class":51,"line":109},[49,1919,1829],{"class":55},[49,1921,371],{"class":76},[49,1923,1924,1927,1930,1932,1934],{"class":51,"line":114},[49,1925,1926],{"class":55},"case",[49,1928,1929],{"class":76}," msg ",[49,1931,349],{"class":55},[49,1933,588],{"class":55},[49,1935,1936],{"class":76},"ch1:\n",[49,1938,1939,1941,1943,1945,1948],{"class":51,"line":126},[49,1940,222],{"class":76},[49,1942,162],{"class":59},[49,1944,165],{"class":76},[49,1946,1947],{"class":83},"\"из ch1:\"",[49,1949,1950],{"class":76},", msg)\n",[49,1952,1953,1955,1957,1959,1961],{"class":51,"line":142},[49,1954,1926],{"class":55},[49,1956,1929],{"class":76},[49,1958,349],{"class":55},[49,1960,588],{"class":55},[49,1962,1963],{"class":76},"ch2:\n",[49,1965,1966,1968,1970,1972,1975],{"class":51,"line":156},[49,1967,222],{"class":76},[49,1969,162],{"class":59},[49,1971,165],{"class":76},[49,1973,1974],{"class":83},"\"из ch2:\"",[49,1976,1950],{"class":76},[49,1978,1979],{"class":51,"line":173},[49,1980,202],{"class":76},[248,1982,1984],{"id":1983},"select-с-default-неблокирующие-операции","select с default — неблокирующие операции",[16,1986,1987,1990],{},[36,1988,1989],{},"default"," выполняется немедленно если ни один канал не готов:",[41,1992,1994],{"className":43,"code":1993,"language":38,"meta":45,"style":45},"ch := make(chan int)\n\nselect {\ncase v := \u003C-ch:\n    fmt.Println(\"получили:\", v)\ndefault:\n    fmt.Println(\"канал не готов, идём дальше\")\n}\n",[36,1995,1996,2012,2016,2022,2035,2049,2055,2068],{"__ignoreMap":45},[49,1997,1998,2000,2002,2004,2006,2008,2010],{"class":51,"line":52},[49,1999,591],{"class":76},[49,2001,349],{"class":55},[49,2003,561],{"class":59},[49,2005,165],{"class":76},[49,2007,566],{"class":55},[49,2009,386],{"class":55},[49,2011,106],{"class":76},[49,2013,2014],{"class":51,"line":63},[49,2015,67],{"emptyLinePlaceholder":66},[49,2017,2018,2020],{"class":51,"line":70},[49,2019,1829],{"class":55},[49,2021,371],{"class":76},[49,2023,2024,2026,2028,2030,2032],{"class":51,"line":80},[49,2025,1926],{"class":55},[49,2027,1389],{"class":76},[49,2029,349],{"class":55},[49,2031,588],{"class":55},[49,2033,2034],{"class":76},"ch:\n",[49,2036,2037,2039,2041,2043,2046],{"class":51,"line":93},[49,2038,222],{"class":76},[49,2040,162],{"class":59},[49,2042,165],{"class":76},[49,2044,2045],{"class":83},"\"получили:\"",[49,2047,2048],{"class":76},", v)\n",[49,2050,2051,2053],{"class":51,"line":103},[49,2052,1989],{"class":55},[49,2054,761],{"class":76},[49,2056,2057,2059,2061,2063,2066],{"class":51,"line":109},[49,2058,222],{"class":76},[49,2060,162],{"class":59},[49,2062,165],{"class":76},[49,2064,2065],{"class":83},"\"канал не готов, идём дальше\"",[49,2067,106],{"class":76},[49,2069,2070],{"class":51,"line":114},[49,2071,202],{"class":76},[248,2073,2075],{"id":2074},"таймаут-через-select","Таймаут через select",[16,2077,2078],{},"Классический паттерн ограничения времени ожидания:",[41,2080,2082],{"className":43,"code":2081,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\ntype Result struct{ Value int }\n\nfunc doHeavyWork() Result {\n    time.Sleep(100 * time.Millisecond)\n    return Result{Value: 42}\n}\n\nfunc main() {\n    ch := make(chan Result)\n    go func() {\n        ch \u003C- doHeavyWork()\n    }()\n\n    select {\n    case result := \u003C-ch:\n        fmt.Println(\"результат:\", result)\n    case \u003C-time.After(5 * time.Second):\n        fmt.Println(\"таймаут — работа заняла слишком долго\")\n    }\n}\n",[36,2083,2084,2090,2094,2100,2108,2116,2120,2124,2142,2146,2160,2176,2191,2195,2199,2207,2223,2231,2242,2246,2250,2258,2273,2288,2310,2324,2330],{"__ignoreMap":45},[49,2085,2086,2088],{"class":51,"line":52},[49,2087,56],{"class":55},[49,2089,60],{"class":59},[49,2091,2092],{"class":51,"line":63},[49,2093,67],{"emptyLinePlaceholder":66},[49,2095,2096,2098],{"class":51,"line":70},[49,2097,73],{"class":55},[49,2099,77],{"class":76},[49,2101,2102,2104,2106],{"class":51,"line":80},[49,2103,84],{"class":83},[49,2105,87],{"class":59},[49,2107,90],{"class":83},[49,2109,2110,2112,2114],{"class":51,"line":93},[49,2111,84],{"class":83},[49,2113,98],{"class":59},[49,2115,90],{"class":83},[49,2117,2118],{"class":51,"line":103},[49,2119,106],{"class":76},[49,2121,2122],{"class":51,"line":109},[49,2123,67],{"emptyLinePlaceholder":66},[49,2125,2126,2128,2131,2133,2136,2139],{"class":51,"line":114},[49,2127,1632],{"class":55},[49,2129,2130],{"class":59}," Result",[49,2132,1638],{"class":55},[49,2134,2135],{"class":76},"{ Value ",[49,2137,2138],{"class":55},"int",[49,2140,2141],{"class":76}," }\n",[49,2143,2144],{"class":51,"line":126},[49,2145,67],{"emptyLinePlaceholder":66},[49,2147,2148,2150,2153,2155,2158],{"class":51,"line":142},[49,2149,117],{"class":55},[49,2151,2152],{"class":59}," doHeavyWork",[49,2154,135],{"class":76},[49,2156,2157],{"class":59},"Result",[49,2159,371],{"class":76},[49,2161,2162,2164,2166,2168,2171,2173],{"class":51,"line":156},[49,2163,187],{"class":76},[49,2165,190],{"class":59},[49,2167,165],{"class":76},[49,2169,2170],{"class":352},"100",[49,2172,491],{"class":55},[49,2174,2175],{"class":76}," time.Millisecond)\n",[49,2177,2178,2181,2183,2186,2189],{"class":51,"line":173},[49,2179,2180],{"class":55},"    return",[49,2182,2130],{"class":59},[49,2184,2185],{"class":76},"{Value: ",[49,2187,2188],{"class":352},"42",[49,2190,202],{"class":76},[49,2192,2193],{"class":51,"line":179},[49,2194,202],{"class":76},[49,2196,2197],{"class":51,"line":184},[49,2198,67],{"emptyLinePlaceholder":66},[49,2200,2201,2203,2205],{"class":51,"line":199},[49,2202,117],{"class":55},[49,2204,120],{"class":59},[49,2206,123],{"class":76},[49,2208,2209,2211,2213,2215,2217,2219,2221],{"class":51,"line":205},[49,2210,556],{"class":76},[49,2212,349],{"class":55},[49,2214,561],{"class":59},[49,2216,165],{"class":76},[49,2218,566],{"class":55},[49,2220,2130],{"class":59},[49,2222,106],{"class":76},[49,2224,2225,2227,2229],{"class":51,"line":210},[49,2226,129],{"class":55},[49,2228,147],{"class":55},[49,2230,123],{"class":76},[49,2232,2233,2236,2238,2240],{"class":51,"line":219},[49,2234,2235],{"class":76},"        ch ",[49,2237,971],{"class":55},[49,2239,2152],{"class":59},[49,2241,769],{"class":76},[49,2243,2244],{"class":51,"line":234},[49,2245,176],{"class":76},[49,2247,2248],{"class":51,"line":1603},[49,2249,67],{"emptyLinePlaceholder":66},[49,2251,2253,2256],{"class":51,"line":2252},21,[49,2254,2255],{"class":55},"    select",[49,2257,371],{"class":76},[49,2259,2261,2264,2267,2269,2271],{"class":51,"line":2260},22,[49,2262,2263],{"class":55},"    case",[49,2265,2266],{"class":76}," result ",[49,2268,349],{"class":55},[49,2270,588],{"class":55},[49,2272,2034],{"class":76},[49,2274,2276,2278,2280,2282,2285],{"class":51,"line":2275},23,[49,2277,159],{"class":76},[49,2279,162],{"class":59},[49,2281,165],{"class":76},[49,2283,2284],{"class":83},"\"результат:\"",[49,2286,2287],{"class":76},", result)\n",[49,2289,2291,2293,2295,2297,2300,2302,2305,2307],{"class":51,"line":2290},24,[49,2292,2263],{"class":55},[49,2294,588],{"class":55},[49,2296,1004],{"class":76},[49,2298,2299],{"class":59},"After",[49,2301,165],{"class":76},[49,2303,2304],{"class":352},"5",[49,2306,491],{"class":55},[49,2308,2309],{"class":76}," time.Second):\n",[49,2311,2313,2315,2317,2319,2322],{"class":51,"line":2312},25,[49,2314,159],{"class":76},[49,2316,162],{"class":59},[49,2318,165],{"class":76},[49,2320,2321],{"class":83},"\"таймаут — работа заняла слишком долго\"",[49,2323,106],{"class":76},[49,2325,2327],{"class":51,"line":2326},26,[49,2328,2329],{"class":76},"    }\n",[49,2331,2333],{"class":51,"line":2332},27,[49,2334,202],{"class":76},[16,2336,2337,2340,2341,2343],{},[36,2338,2339],{},"time.After"," возвращает канал, в который придёт значение через указанное время. В паре с ",[36,2342,1829],{}," это идиоматичный таймаут в Go.",[248,2345,2347],{"id":2346},"бесконечный-цикл-с-select","Бесконечный цикл с select",[16,2349,2350],{},"Типичный паттерн для горутины-воркера:",[41,2352,2354],{"className":43,"code":2353,"language":38,"meta":45,"style":45},"package main\n\nimport \"fmt\"\n\ntype Job struct{ ID int }\ntype Result struct{ Value int }\n\nfunc process(j Job) Result { return Result{Value: j.ID * 2} }\n\nfunc worker(jobs \u003C-chan Job, results chan\u003C- Result, quit \u003C-chan struct{}) {\n    for {\n        select {\n        case job := \u003C-jobs:\n            results \u003C- process(job)\n        case \u003C-quit:\n            fmt.Println(\"воркер завершается\")\n            return\n        }\n    }\n}\n\nfunc main() {\n    jobs := make(chan Job, 3)\n    results := make(chan Result, 3)\n    quit := make(chan struct{})\n\n    go worker(jobs, results, quit)\n\n    jobs \u003C- Job{ID: 1}\n    jobs \u003C- Job{ID: 2}\n    jobs \u003C- Job{ID: 3}\n    close(jobs)\n\n    fmt.Println(\u003C-results)\n    fmt.Println(\u003C-results)\n    fmt.Println(\u003C-results)\n    close(quit)\n}\n",[36,2355,2356,2362,2366,2376,2380,2396,2410,2414,2452,2456,2493,2500,2507,2522,2534,2543,2557,2562,2566,2570,2574,2578,2586,2607,2628,2646,2650,2659,2664,2681,2697,2712,2721,2726,2740,2753,2766,2774],{"__ignoreMap":45},[49,2357,2358,2360],{"class":51,"line":52},[49,2359,56],{"class":55},[49,2361,60],{"class":59},[49,2363,2364],{"class":51,"line":63},[49,2365,67],{"emptyLinePlaceholder":66},[49,2367,2368,2370,2372,2374],{"class":51,"line":70},[49,2369,73],{"class":55},[49,2371,1445],{"class":83},[49,2373,87],{"class":59},[49,2375,90],{"class":83},[49,2377,2378],{"class":51,"line":80},[49,2379,67],{"emptyLinePlaceholder":66},[49,2381,2382,2384,2387,2389,2392,2394],{"class":51,"line":93},[49,2383,1632],{"class":55},[49,2385,2386],{"class":59}," Job",[49,2388,1638],{"class":55},[49,2390,2391],{"class":76},"{ ID ",[49,2393,2138],{"class":55},[49,2395,2141],{"class":76},[49,2397,2398,2400,2402,2404,2406,2408],{"class":51,"line":103},[49,2399,1632],{"class":55},[49,2401,2130],{"class":59},[49,2403,1638],{"class":55},[49,2405,2135],{"class":76},[49,2407,2138],{"class":55},[49,2409,2141],{"class":76},[49,2411,2412],{"class":51,"line":109},[49,2413,67],{"emptyLinePlaceholder":66},[49,2415,2416,2418,2421,2423,2426,2428,2431,2433,2436,2439,2441,2444,2447,2449],{"class":51,"line":114},[49,2417,117],{"class":55},[49,2419,2420],{"class":59}," process",[49,2422,165],{"class":76},[49,2424,2425],{"class":382},"j",[49,2427,2386],{"class":59},[49,2429,2430],{"class":76},") ",[49,2432,2157],{"class":59},[49,2434,2435],{"class":76}," { ",[49,2437,2438],{"class":55},"return",[49,2440,2130],{"class":59},[49,2442,2443],{"class":76},"{Value: j.ID ",[49,2445,2446],{"class":55},"*",[49,2448,1141],{"class":352},[49,2450,2451],{"class":76},"} }\n",[49,2453,2454],{"class":51,"line":126},[49,2455,67],{"emptyLinePlaceholder":66},[49,2457,2458,2460,2463,2465,2468,2470,2472,2474,2477,2479,2481,2483,2486,2488,2490],{"class":51,"line":142},[49,2459,117],{"class":55},[49,2461,2462],{"class":59}," worker",[49,2464,165],{"class":76},[49,2466,2467],{"class":382},"jobs",[49,2469,1515],{"class":55},[49,2471,2386],{"class":59},[49,2473,899],{"class":76},[49,2475,2476],{"class":382},"results",[49,2478,1468],{"class":55},[49,2480,2130],{"class":59},[49,2482,899],{"class":76},[49,2484,2485],{"class":382},"quit",[49,2487,1515],{"class":55},[49,2489,1638],{"class":55},[49,2491,2492],{"class":76},"{}) {\n",[49,2494,2495,2498],{"class":51,"line":156},[49,2496,2497],{"class":55},"    for",[49,2499,371],{"class":76},[49,2501,2502,2505],{"class":51,"line":173},[49,2503,2504],{"class":55},"        select",[49,2506,371],{"class":76},[49,2508,2509,2512,2515,2517,2519],{"class":51,"line":179},[49,2510,2511],{"class":55},"        case",[49,2513,2514],{"class":76}," job ",[49,2516,349],{"class":55},[49,2518,588],{"class":55},[49,2520,2521],{"class":76},"jobs:\n",[49,2523,2524,2527,2529,2531],{"class":51,"line":184},[49,2525,2526],{"class":76},"            results ",[49,2528,971],{"class":55},[49,2530,2420],{"class":59},[49,2532,2533],{"class":76},"(job)\n",[49,2535,2536,2538,2540],{"class":51,"line":199},[49,2537,2511],{"class":55},[49,2539,588],{"class":55},[49,2541,2542],{"class":76},"quit:\n",[49,2544,2545,2548,2550,2552,2555],{"class":51,"line":205},[49,2546,2547],{"class":76},"            fmt.",[49,2549,162],{"class":59},[49,2551,165],{"class":76},[49,2553,2554],{"class":83},"\"воркер завершается\"",[49,2556,106],{"class":76},[49,2558,2559],{"class":51,"line":210},[49,2560,2561],{"class":55},"            return\n",[49,2563,2564],{"class":51,"line":219},[49,2565,665],{"class":76},[49,2567,2568],{"class":51,"line":234},[49,2569,2329],{"class":76},[49,2571,2572],{"class":51,"line":1603},[49,2573,202],{"class":76},[49,2575,2576],{"class":51,"line":2252},[49,2577,67],{"emptyLinePlaceholder":66},[49,2579,2580,2582,2584],{"class":51,"line":2260},[49,2581,117],{"class":55},[49,2583,120],{"class":59},[49,2585,123],{"class":76},[49,2587,2588,2591,2593,2595,2597,2599,2601,2603,2605],{"class":51,"line":2275},[49,2589,2590],{"class":76},"    jobs ",[49,2592,349],{"class":55},[49,2594,561],{"class":59},[49,2596,165],{"class":76},[49,2598,566],{"class":55},[49,2600,2386],{"class":59},[49,2602,899],{"class":76},[49,2604,1114],{"class":352},[49,2606,106],{"class":76},[49,2608,2609,2612,2614,2616,2618,2620,2622,2624,2626],{"class":51,"line":2290},[49,2610,2611],{"class":76},"    results ",[49,2613,349],{"class":55},[49,2615,561],{"class":59},[49,2617,165],{"class":76},[49,2619,566],{"class":55},[49,2621,2130],{"class":59},[49,2623,899],{"class":76},[49,2625,1114],{"class":352},[49,2627,106],{"class":76},[49,2629,2630,2633,2635,2637,2639,2641,2643],{"class":51,"line":2312},[49,2631,2632],{"class":76},"    quit ",[49,2634,349],{"class":55},[49,2636,561],{"class":59},[49,2638,165],{"class":76},[49,2640,566],{"class":55},[49,2642,1638],{"class":55},[49,2644,2645],{"class":76},"{})\n",[49,2647,2648],{"class":51,"line":2326},[49,2649,67],{"emptyLinePlaceholder":66},[49,2651,2652,2654,2656],{"class":51,"line":2332},[49,2653,129],{"class":55},[49,2655,2462],{"class":59},[49,2657,2658],{"class":76},"(jobs, results, quit)\n",[49,2660,2662],{"class":51,"line":2661},28,[49,2663,67],{"emptyLinePlaceholder":66},[49,2665,2667,2669,2671,2673,2676,2679],{"class":51,"line":2666},29,[49,2668,2590],{"class":76},[49,2670,971],{"class":55},[49,2672,2386],{"class":59},[49,2674,2675],{"class":76},"{ID: ",[49,2677,2678],{"class":352},"1",[49,2680,202],{"class":76},[49,2682,2684,2686,2688,2690,2692,2695],{"class":51,"line":2683},30,[49,2685,2590],{"class":76},[49,2687,971],{"class":55},[49,2689,2386],{"class":59},[49,2691,2675],{"class":76},[49,2693,2694],{"class":352},"2",[49,2696,202],{"class":76},[49,2698,2700,2702,2704,2706,2708,2710],{"class":51,"line":2699},31,[49,2701,2590],{"class":76},[49,2703,971],{"class":55},[49,2705,2386],{"class":59},[49,2707,2675],{"class":76},[49,2709,1114],{"class":352},[49,2711,202],{"class":76},[49,2713,2715,2718],{"class":51,"line":2714},32,[49,2716,2717],{"class":59},"    close",[49,2719,2720],{"class":76},"(jobs)\n",[49,2722,2724],{"class":51,"line":2723},33,[49,2725,67],{"emptyLinePlaceholder":66},[49,2727,2729,2731,2733,2735,2737],{"class":51,"line":2728},34,[49,2730,222],{"class":76},[49,2732,162],{"class":59},[49,2734,165],{"class":76},[49,2736,971],{"class":55},[49,2738,2739],{"class":76},"results)\n",[49,2741,2743,2745,2747,2749,2751],{"class":51,"line":2742},35,[49,2744,222],{"class":76},[49,2746,162],{"class":59},[49,2748,165],{"class":76},[49,2750,971],{"class":55},[49,2752,2739],{"class":76},[49,2754,2756,2758,2760,2762,2764],{"class":51,"line":2755},36,[49,2757,222],{"class":76},[49,2759,162],{"class":59},[49,2761,165],{"class":76},[49,2763,971],{"class":55},[49,2765,2739],{"class":76},[49,2767,2769,2771],{"class":51,"line":2768},37,[49,2770,2717],{"class":59},[49,2772,2773],{"class":76},"(quit)\n",[49,2775,2777],{"class":51,"line":2776},38,[49,2778,202],{"class":76},[25,2780],{},[28,2782,2784],{"id":2783},"синхронизация-через-каналы","Синхронизация через каналы",[16,2786,2787],{},"Вернёмся к проблеме из начала статьи — правильное ожидание горутин:",[248,2789,2791],{"id":2790},"ожидание-завершения-через-канал-сигнал","Ожидание завершения через канал-сигнал",[41,2793,2795],{"className":43,"code":2794,"language":38,"meta":45,"style":45},"done := make(chan struct{})\n\ngo func() {\n    doWork()\n    close(done) \u002F\u002F сигнализируем о завершении\n}()\n\n\u003C-done \u002F\u002F блокируемся до закрытия канала\n",[36,2796,2797,2814,2818,2826,2833,2843,2847,2851],{"__ignoreMap":45},[49,2798,2799,2802,2804,2806,2808,2810,2812],{"class":51,"line":52},[49,2800,2801],{"class":76},"done ",[49,2803,349],{"class":55},[49,2805,561],{"class":59},[49,2807,165],{"class":76},[49,2809,566],{"class":55},[49,2811,1638],{"class":55},[49,2813,2645],{"class":76},[49,2815,2816],{"class":51,"line":63},[49,2817,67],{"emptyLinePlaceholder":66},[49,2819,2820,2822,2824],{"class":51,"line":70},[49,2821,38],{"class":55},[49,2823,147],{"class":55},[49,2825,123],{"class":76},[49,2827,2828,2831],{"class":51,"line":80},[49,2829,2830],{"class":59},"    doWork",[49,2832,769],{"class":76},[49,2834,2835,2837,2840],{"class":51,"line":93},[49,2836,2717],{"class":59},[49,2838,2839],{"class":76},"(done) ",[49,2841,2842],{"class":138},"\u002F\u002F сигнализируем о завершении\n",[49,2844,2845],{"class":51,"line":103},[49,2846,995],{"class":76},[49,2848,2849],{"class":51,"line":109},[49,2850,67],{"emptyLinePlaceholder":66},[49,2852,2853,2855,2857],{"class":51,"line":114},[49,2854,971],{"class":55},[49,2856,2801],{"class":76},[49,2858,2859],{"class":138},"\u002F\u002F блокируемся до закрытия канала\n",[16,2861,2862,2865],{},[36,2863,2864],{},"struct{}"," как тип канала — идиоматично для сигналов: нулевой размер, ничего не передаём, только факт события.",[248,2867,2869],{"id":2868},"waitgroup-для-нескольких-горутин","WaitGroup для нескольких горутин",[16,2871,2872,2873,2875],{},"Когда горутин несколько — ",[36,2874,245],{}," чище каналов:",[41,2877,2879],{"className":43,"code":2878,"language":38,"meta":45,"style":45},"var wg sync.WaitGroup\n\nfor i := 0; i \u003C 5; i++ {\n    wg.Add(1) \u002F\u002F инкремент до запуска горутины — важно!\n    go func(n int) {\n        defer wg.Done() \u002F\u002F декремент при завершении\n        fmt.Println(\"горутина\", n)\n    }(i)\n}\n\nwg.Wait() \u002F\u002F ждём пока счётчик не станет 0\n",[36,2880,2881,2897,2901,2924,2941,2955,2970,2984,2988,2992,2996],{"__ignoreMap":45},[49,2882,2883,2886,2889,2892,2894],{"class":51,"line":52},[49,2884,2885],{"class":55},"var",[49,2887,2888],{"class":76}," wg ",[49,2890,2891],{"class":59},"sync",[49,2893,703],{"class":76},[49,2895,2896],{"class":59},"WaitGroup\n",[49,2898,2899],{"class":51,"line":63},[49,2900,67],{"emptyLinePlaceholder":66},[49,2902,2903,2905,2907,2909,2911,2913,2915,2918,2920,2922],{"class":51,"line":70},[49,2904,343],{"class":55},[49,2906,346],{"class":76},[49,2908,349],{"class":55},[49,2910,353],{"class":352},[49,2912,356],{"class":76},[49,2914,359],{"class":55},[49,2916,2917],{"class":352}," 5",[49,2919,365],{"class":76},[49,2921,368],{"class":55},[49,2923,371],{"class":76},[49,2925,2926,2929,2932,2934,2936,2938],{"class":51,"line":80},[49,2927,2928],{"class":76},"    wg.",[49,2930,2931],{"class":59},"Add",[49,2933,165],{"class":76},[49,2935,2678],{"class":352},[49,2937,2430],{"class":76},[49,2939,2940],{"class":138},"\u002F\u002F инкремент до запуска горутины — важно!\n",[49,2942,2943,2945,2947,2949,2951,2953],{"class":51,"line":93},[49,2944,129],{"class":55},[49,2946,147],{"class":55},[49,2948,165],{"class":76},[49,2950,383],{"class":382},[49,2952,386],{"class":55},[49,2954,389],{"class":76},[49,2956,2957,2960,2963,2965,2967],{"class":51,"line":103},[49,2958,2959],{"class":55},"        defer",[49,2961,2962],{"class":76}," wg.",[49,2964,742],{"class":59},[49,2966,135],{"class":76},[49,2968,2969],{"class":138},"\u002F\u002F декремент при завершении\n",[49,2971,2972,2974,2976,2978,2981],{"class":51,"line":109},[49,2973,159],{"class":76},[49,2975,162],{"class":59},[49,2977,165],{"class":76},[49,2979,2980],{"class":83},"\"горутина\"",[49,2982,2983],{"class":76},", n)\n",[49,2985,2986],{"class":51,"line":114},[49,2987,399],{"class":76},[49,2989,2990],{"class":51,"line":126},[49,2991,202],{"class":76},[49,2993,2994],{"class":51,"line":142},[49,2995,67],{"emptyLinePlaceholder":66},[49,2997,2998,3001,3004,3006],{"class":51,"line":156},[49,2999,3000],{"class":76},"wg.",[49,3002,3003],{"class":59},"Wait",[49,3005,135],{"class":76},[49,3007,3008],{"class":138},"\u002F\u002F ждём пока счётчик не станет 0\n",[16,3010,3011,3014,3015,3017,3018,3021,3022,703],{},[36,3012,3013],{},"wg.Add(1)"," всегда должен вызываться до ",[36,3016,38],{},", а не внутри горутины. Иначе возможна гонка: ",[36,3019,3020],{},"wg.Wait()"," может отработать до того, как горутина успеет вызвать ",[36,3023,2931],{},[25,3025],{},[28,3027,3029],{"id":3028},"deadlock","Deadlock",[16,3031,3032],{},"Deadlock — ситуация, когда все горутины заблокированы и прогресс невозможен. Go детектирует это в рантайме:",[41,3034,3036],{"className":43,"code":3035,"language":38,"meta":45,"style":45},"func main() {\n    ch := make(chan int)\n    ch \u003C- 1 \u002F\u002F main блокируется — некому читать\n    \u002F\u002F fatal error: all goroutines are asleep - deadlock!\n}\n",[36,3037,3038,3046,3062,3073,3078],{"__ignoreMap":45},[49,3039,3040,3042,3044],{"class":51,"line":52},[49,3041,117],{"class":55},[49,3043,120],{"class":59},[49,3045,123],{"class":76},[49,3047,3048,3050,3052,3054,3056,3058,3060],{"class":51,"line":63},[49,3049,556],{"class":76},[49,3051,349],{"class":55},[49,3053,561],{"class":59},[49,3055,165],{"class":76},[49,3057,566],{"class":55},[49,3059,386],{"class":55},[49,3061,106],{"class":76},[49,3063,3064,3066,3068,3070],{"class":51,"line":70},[49,3065,556],{"class":76},[49,3067,971],{"class":55},[49,3069,1129],{"class":352},[49,3071,3072],{"class":138}," \u002F\u002F main блокируется — некому читать\n",[49,3074,3075],{"class":51,"line":80},[49,3076,3077],{"class":138},"    \u002F\u002F fatal error: all goroutines are asleep - deadlock!\n",[49,3079,3080],{"class":51,"line":93},[49,3081,202],{"class":76},[41,3083,3085],{"className":43,"code":3084,"language":38,"meta":45,"style":45},"\u002F\u002F Взаимный deadlock двух горутин\nch1 := make(chan int)\nch2 := make(chan int)\n\ngo func() {\n    ch1 \u003C- 1  \u002F\u002F ждёт ch2 \u003C- из второй горутины\n    \u003C-ch2\n}()\n\ngo func() {\n    ch2 \u003C- 2  \u002F\u002F ждёт ch1 \u003C- из первой горутины\n    \u003C-ch1\n}()\n\n\u002F\u002F Обе горутины ждут друг друга — deadlock\n",[36,3086,3087,3092,3108,3124,3128,3136,3148,3156,3160,3164,3172,3184,3191,3195,3199],{"__ignoreMap":45},[49,3088,3089],{"class":51,"line":52},[49,3090,3091],{"class":138},"\u002F\u002F Взаимный deadlock двух горутин\n",[49,3093,3094,3096,3098,3100,3102,3104,3106],{"class":51,"line":63},[49,3095,1844],{"class":76},[49,3097,349],{"class":55},[49,3099,561],{"class":59},[49,3101,165],{"class":76},[49,3103,566],{"class":55},[49,3105,386],{"class":55},[49,3107,106],{"class":76},[49,3109,3110,3112,3114,3116,3118,3120,3122],{"class":51,"line":70},[49,3111,1862],{"class":76},[49,3113,349],{"class":55},[49,3115,561],{"class":59},[49,3117,165],{"class":76},[49,3119,566],{"class":55},[49,3121,386],{"class":55},[49,3123,106],{"class":76},[49,3125,3126],{"class":51,"line":80},[49,3127,67],{"emptyLinePlaceholder":66},[49,3129,3130,3132,3134],{"class":51,"line":93},[49,3131,38],{"class":55},[49,3133,147],{"class":55},[49,3135,123],{"class":76},[49,3137,3138,3141,3143,3145],{"class":51,"line":103},[49,3139,3140],{"class":76},"    ch1 ",[49,3142,971],{"class":55},[49,3144,1129],{"class":352},[49,3146,3147],{"class":138},"  \u002F\u002F ждёт ch2 \u003C- из второй горутины\n",[49,3149,3150,3153],{"class":51,"line":109},[49,3151,3152],{"class":55},"    \u003C-",[49,3154,3155],{"class":76},"ch2\n",[49,3157,3158],{"class":51,"line":114},[49,3159,995],{"class":76},[49,3161,3162],{"class":51,"line":126},[49,3163,67],{"emptyLinePlaceholder":66},[49,3165,3166,3168,3170],{"class":51,"line":142},[49,3167,38],{"class":55},[49,3169,147],{"class":55},[49,3171,123],{"class":76},[49,3173,3174,3177,3179,3181],{"class":51,"line":156},[49,3175,3176],{"class":76},"    ch2 ",[49,3178,971],{"class":55},[49,3180,1141],{"class":352},[49,3182,3183],{"class":138},"  \u002F\u002F ждёт ch1 \u003C- из первой горутины\n",[49,3185,3186,3188],{"class":51,"line":173},[49,3187,3152],{"class":55},[49,3189,3190],{"class":76},"ch1\n",[49,3192,3193],{"class":51,"line":179},[49,3194,995],{"class":76},[49,3196,3197],{"class":51,"line":184},[49,3198,67],{"emptyLinePlaceholder":66},[49,3200,3201],{"class":51,"line":199},[49,3202,3203],{"class":138},"\u002F\u002F Обе горутины ждут друг друга — deadlock\n",[16,3205,3206,3207,3210],{},"Go детектирует только ",[325,3208,3209],{},"полный"," deadlock (все горутины заблокированы). Частичный — когда часть горутин заблокирована вечно — это утечка, и детектора для неё нет.",[25,3212],{},[28,3214,3216],{"id":3215},"распространённые-ошибки","Распространённые ошибки",[248,3218,3220],{"id":3219},"захват-переменной-цикла","Захват переменной цикла",[41,3222,3224],{"className":43,"code":3223,"language":38,"meta":418,"style":45},"\u002F\u002F Ошибка: все горутины захватывают одну переменную i,\n\u002F\u002F если она объявлена снаружи цикла\nvar i int\nfor i = 0; i \u003C 5; i++ {\n    go func() {\n        fmt.Println(i) \u002F\u002F скорее всего напечатает 5 5 5 5 5\n    }()\n}\n\n\u002F\u002F Правильно: передаём значение через аргумент\nfor i := 0; i \u003C 5; i++ {\n    go func(n int) {\n        fmt.Println(n) \u002F\u002F 0 1 2 3 4 (в неопределённом порядке)\n    }(i)\n}\n\n\u002F\u002F В Go 1.22+ это тоже правильно: переменная, объявленная\n\u002F\u002F прямо в for i := ..., создаётся заново на каждой итерации.\n",[36,3225,3226,3231,3236,3245,3267,3275,3287,3291,3295,3299,3304,3326,3340,3352,3356,3360,3364,3369],{"__ignoreMap":45},[49,3227,3228],{"class":51,"line":52},[49,3229,3230],{"class":138},"\u002F\u002F Ошибка: все горутины захватывают одну переменную i,\n",[49,3232,3233],{"class":51,"line":63},[49,3234,3235],{"class":138},"\u002F\u002F если она объявлена снаружи цикла\n",[49,3237,3238,3240,3242],{"class":51,"line":70},[49,3239,2885],{"class":55},[49,3241,346],{"class":76},[49,3243,3244],{"class":55},"int\n",[49,3246,3247,3249,3251,3253,3255,3257,3259,3261,3263,3265],{"class":51,"line":80},[49,3248,343],{"class":55},[49,3250,346],{"class":76},[49,3252,1333],{"class":55},[49,3254,353],{"class":352},[49,3256,356],{"class":76},[49,3258,359],{"class":55},[49,3260,2917],{"class":352},[49,3262,365],{"class":76},[49,3264,368],{"class":55},[49,3266,371],{"class":76},[49,3268,3269,3271,3273],{"class":51,"line":93},[49,3270,129],{"class":55},[49,3272,147],{"class":55},[49,3274,123],{"class":76},[49,3276,3277,3279,3281,3284],{"class":51,"line":103},[49,3278,159],{"class":76},[49,3280,162],{"class":59},[49,3282,3283],{"class":76},"(i) ",[49,3285,3286],{"class":138},"\u002F\u002F скорее всего напечатает 5 5 5 5 5\n",[49,3288,3289],{"class":51,"line":109},[49,3290,176],{"class":76},[49,3292,3293],{"class":51,"line":114},[49,3294,202],{"class":76},[49,3296,3297],{"class":51,"line":126},[49,3298,67],{"emptyLinePlaceholder":66},[49,3300,3301],{"class":51,"line":142},[49,3302,3303],{"class":138},"\u002F\u002F Правильно: передаём значение через аргумент\n",[49,3305,3306,3308,3310,3312,3314,3316,3318,3320,3322,3324],{"class":51,"line":156},[49,3307,343],{"class":55},[49,3309,346],{"class":76},[49,3311,349],{"class":55},[49,3313,353],{"class":352},[49,3315,356],{"class":76},[49,3317,359],{"class":55},[49,3319,2917],{"class":352},[49,3321,365],{"class":76},[49,3323,368],{"class":55},[49,3325,371],{"class":76},[49,3327,3328,3330,3332,3334,3336,3338],{"class":51,"line":173},[49,3329,129],{"class":55},[49,3331,147],{"class":55},[49,3333,165],{"class":76},[49,3335,383],{"class":382},[49,3337,386],{"class":55},[49,3339,389],{"class":76},[49,3341,3342,3344,3346,3349],{"class":51,"line":179},[49,3343,159],{"class":76},[49,3345,162],{"class":59},[49,3347,3348],{"class":76},"(n) ",[49,3350,3351],{"class":138},"\u002F\u002F 0 1 2 3 4 (в неопределённом порядке)\n",[49,3353,3354],{"class":51,"line":184},[49,3355,399],{"class":76},[49,3357,3358],{"class":51,"line":199},[49,3359,202],{"class":76},[49,3361,3362],{"class":51,"line":205},[49,3363,67],{"emptyLinePlaceholder":66},[49,3365,3366],{"class":51,"line":210},[49,3367,3368],{"class":138},"\u002F\u002F В Go 1.22+ это тоже правильно: переменная, объявленная\n",[49,3370,3371],{"class":51,"line":219},[49,3372,3373],{"class":138},"\u002F\u002F прямо в for i := ..., создаётся заново на каждой итерации.\n",[248,3375,3377],{"id":3376},"запись-в-закрытый-канал","Запись в закрытый канал",[41,3379,3381],{"className":43,"code":3380,"language":38,"meta":45,"style":45},"ch := make(chan int)\nclose(ch)\nch \u003C- 1 \u002F\u002F panic: send on closed channel\n",[36,3382,3383,3399,3405],{"__ignoreMap":45},[49,3384,3385,3387,3389,3391,3393,3395,3397],{"class":51,"line":52},[49,3386,591],{"class":76},[49,3388,349],{"class":55},[49,3390,561],{"class":59},[49,3392,165],{"class":76},[49,3394,566],{"class":55},[49,3396,386],{"class":55},[49,3398,106],{"class":76},[49,3400,3401,3403],{"class":51,"line":63},[49,3402,1225],{"class":59},[49,3404,1289],{"class":76},[49,3406,3407,3409,3411,3413],{"class":51,"line":70},[49,3408,591],{"class":76},[49,3410,971],{"class":55},[49,3412,1129],{"class":352},[49,3414,3415],{"class":138}," \u002F\u002F panic: send on closed channel\n",[248,3417,3419],{"id":3418},"паника-при-закрытии-уже-закрытого-канала","Паника при закрытии уже закрытого канала",[41,3421,3423],{"className":43,"code":3422,"language":38,"meta":45,"style":45},"ch := make(chan int)\nclose(ch)\nclose(ch) \u002F\u002F panic: close of closed channel\n\n\u002F\u002F Решение: sync.Once для гарантированно однократного закрытия\nvar once sync.Once\ncloseOnce := func() { once.Do(func() { close(ch) }) }\n",[36,3424,3425,3441,3447,3456,3460,3465,3479],{"__ignoreMap":45},[49,3426,3427,3429,3431,3433,3435,3437,3439],{"class":51,"line":52},[49,3428,591],{"class":76},[49,3430,349],{"class":55},[49,3432,561],{"class":59},[49,3434,165],{"class":76},[49,3436,566],{"class":55},[49,3438,386],{"class":55},[49,3440,106],{"class":76},[49,3442,3443,3445],{"class":51,"line":63},[49,3444,1225],{"class":59},[49,3446,1289],{"class":76},[49,3448,3449,3451,3453],{"class":51,"line":70},[49,3450,1225],{"class":59},[49,3452,1590],{"class":76},[49,3454,3455],{"class":138},"\u002F\u002F panic: close of closed channel\n",[49,3457,3458],{"class":51,"line":80},[49,3459,67],{"emptyLinePlaceholder":66},[49,3461,3462],{"class":51,"line":93},[49,3463,3464],{"class":138},"\u002F\u002F Решение: sync.Once для гарантированно однократного закрытия\n",[49,3466,3467,3469,3472,3474,3476],{"class":51,"line":103},[49,3468,2885],{"class":55},[49,3470,3471],{"class":76}," once ",[49,3473,2891],{"class":59},[49,3475,703],{"class":76},[49,3477,3478],{"class":59},"Once\n",[49,3480,3481,3484,3486,3488,3491,3494,3496,3498,3501,3503],{"class":51,"line":109},[49,3482,3483],{"class":76},"closeOnce ",[49,3485,349],{"class":55},[49,3487,147],{"class":55},[49,3489,3490],{"class":76},"() { once.",[49,3492,3493],{"class":59},"Do",[49,3495,165],{"class":76},[49,3497,117],{"class":55},[49,3499,3500],{"class":76},"() { ",[49,3502,1225],{"class":59},[49,3504,3505],{"class":76},"(ch) }) }\n",[25,3507],{},[28,3509,3511],{"id":3510},"вопросы-на-собеседовании","Вопросы на собеседовании",[16,3513,3514,3517,3520],{},[325,3515,3516],{},"Q: Чем горутина отличается от OS-потока?",[3518,3519],"br",{},"\nA: Горутина управляется рантаймом Go, а не ОС. Начальный стек — 2–8 КБ против 1–8 МБ у потока, создание — микросекунды против десятков микросекунд. Стек горутины растёт динамически. Тысячи горутин — норма, тысячи OS-потоков — проблема.",[16,3522,3523,3526,3528,3529,3531,3532,3535,3536,3539],{},[325,3524,3525],{},"Q: Что такое утечка горутины? Как обнаружить?",[3518,3527],{},"\nA: Горутина запущена, но никогда не завершится — заблокирована на канале, мьютексе или в бесконечном цикле без условия выхода. Держит память и стек. Обнаружить: ",[36,3530,792],{}," в тестах, ",[36,3533,3534],{},"runtime.NumGoroutine()"," в метриках, ",[36,3537,3538],{},"pprof"," в продакшне.",[16,3541,3542,3545,3547],{},[325,3543,3544],{},"Q: Чем буферизованный канал отличается от небуферизованного?",[3518,3546],{},"\nA: Небуферизованный — рандеву: отправитель блокируется пока получатель не готов, и наоборот. Буферизованный — асинхронная очередь FIFO: отправка не блокируется пока буфер не заполнен. Небуферизованный — строгая синхронизация, буферизованный — сглаживание пиков.",[16,3549,3550,3553,3555,3556,3559],{},[325,3551,3552],{},"Q: Что произойдёт при чтении из закрытого канала?",[3518,3554],{},"\nA: Немедленно вернётся zero value и ",[36,3557,3558],{},"false"," в двухаргументной форме. Если в канале ещё есть данные — сначала вернут их. Запись в закрытый канал — паника. Повторное закрытие — паника.",[16,3561,3562,3565,3567,3568,3570],{},[325,3563,3564],{},"Q: Кто должен закрывать канал?",[3518,3566],{},"\nA: Тот, кто пишет — отправитель. Читатель не знает, будет ли ещё запись, и закрытие с его стороны вызовет панику при следующей записи. Если отправителей несколько — используют ",[36,3569,245],{}," + отдельную горутину, которая ждёт всех и закрывает.",[16,3572,3573,3576,3578],{},[325,3574,3575],{},"Q: Что такое select? Что если несколько кейсов готовы одновременно?",[3518,3577],{},"\nA: Конструкция для мультиплексирования каналов — ждёт пока хотя бы один кейс станет готов. Если готовы несколько — выбирает случайный. Это намеренная рандомизация, чтобы не было систематического голодания одного канала.",[16,3580,3581,3584,3586,3587,3589,3590,3592,3593,3595,3596,3598,3599,3601],{},[325,3582,3583],{},"Q: Как сделать неблокирующую отправку или чтение из канала?",[3518,3585],{},"\nA: Через ",[36,3588,1829],{}," с ",[36,3591,1989],{},": если канал не готов — немедленно выполняется ",[36,3594,1989],{},". Без ",[36,3597,1989],{}," ",[36,3600,1829],{}," ждёт.",[16,3603,3604,3607,3609,3610,3612,3613,3615,3616,3619,3620,3622,3623,703],{},[325,3605,3606],{},"Q: Как правильно ждать завершения нескольких горутин?",[3518,3608],{},"\nA: ",[36,3611,245],{},": ",[36,3614,3013],{}," до запуска горутины, ",[36,3617,3618],{},"defer wg.Done()"," внутри, ",[36,3621,3020],{}," в основной горутине. Альтернатива для простых случаев — канал-сигнал ",[36,3624,3625],{},"chan struct{}",[16,3627,3628,3637,3639,3640,3642,3643,3645,3646,3648,3649,3651],{},[325,3629,3630,3631,3633,3634,3636],{},"Q: Почему ",[36,3632,3013],{}," нужно вызывать до ",[36,3635,38],{},", а не внутри горутины?",[3518,3638],{},"\nA: Гонка данных: ",[36,3641,3020],{}," может выполниться до того, как горутина успеет вызвать ",[36,3644,2931],{},". Если ",[36,3647,2931],{}," внутри горутины — счётчик может оставаться нулевым в момент ",[36,3650,3003],{},", и программа завершится не дождавшись горутин.",[16,3653,3654,3657,3659,3660,3663],{},[325,3655,3656],{},"Q: Что такое deadlock? Как Go его обнаруживает?",[3518,3658],{},"\nA: Ситуация когда все горутины заблокированы и прогресс невозможен. Go детектирует полный deadlock в рантайме: ",[36,3661,3662],{},"fatal error: all goroutines are asleep - deadlock!",". Частичный deadlock (часть горутин заблокирована вечно) — это утечка, детектора нет.",[16,3665,3666,3669,3671,3672,3675,3676,3679],{},[325,3667,3668],{},"Q: В чём проблема захвата переменной цикла в горутине?",[3518,3670],{},"\nA: Горутины захватывают переменную, а не снимок значения. Если переменная объявлена снаружи цикла, к моменту исполнения горутины она может уже содержать последнее значение. Решение: передавать значение как аргумент ",[36,3673,3674],{},"go func(n int){...}(i)",". В Go 1.22+ переменная, объявленная прямо в ",[36,3677,3678],{},"for i := ...",", создаётся заново на каждой итерации, поэтому старая ловушка проявляется только в legacy-коде или при внешней переменной.",[11,3681,3683],{"id":3682},"задачи-горутины-и-каналы","Задачи: Горутины и каналы",[25,3685],{},[16,3687,3688],{},[325,3689,3690],{},"Задача 1: Конкурентный сбор результатов",[16,3692,3693,3696],{},[325,3694,3695],{},"Уровень:"," Лёгкая",[16,3698,3699,3702],{},[325,3700,3701],{},"Что проверяет:"," базовый запуск горутин, сбор результатов через канал",[16,3704,3705,3708,3709,3712],{},[325,3706,3707],{},"Условие:"," Напиши функцию ",[36,3710,3711],{},"squareConcurrent(nums []int) []int"," которая возводит каждое число в квадрат конкурентно (каждое число в отдельной горутине) и возвращает результаты. Порядок результатов должен совпадать с порядком входных данных.",[16,3714,3715],{},[325,3716,3717],{},"Примеры:",[41,3719,3724],{"className":3720,"code":3722,"language":3723},[3721],"language-text","squareConcurrent([]int{1, 2, 3, 4, 5}) → [1 4 9 16 25]\n","text",[36,3725,3722],{"__ignoreMap":45},[16,3727,3728],{},[325,3729,3730],{},"Решение:",[41,3732,3734],{"className":43,"code":3733,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"fmt\"\n    \"sync\"\n)\n\nfunc squareConcurrent(nums []int) []int {\n    result := make([]int, len(nums))\n    var wg sync.WaitGroup\n\n    for i, n := range nums {\n        wg.Add(1)\n        go func(idx, val int) {\n            defer wg.Done()\n            result[idx] = val * val \u002F\u002F каждая горутина пишет в свой индекс\n        }(i, n)\n    }\n\n    wg.Wait()\n    return result\n}\n\nfunc main() {\n    fmt.Println(squareConcurrent([]int{1, 2, 3, 4, 5})) \u002F\u002F [1 4 9 16 25]\n}\n\n\u002F\u002F Безопасно без мьютекса: каждая горутина пишет\n\u002F\u002F в уникальный индекс — нет гонки данных.\n",[36,3735,3736,3742,3746,3752,3760,3768,3772,3776,3800,3822,3835,3839,3853,3866,3887,3898,3915,3920,3924,3928,3936,3943,3947,3951,3959,4002,4006,4010,4015],{"__ignoreMap":45},[49,3737,3738,3740],{"class":51,"line":52},[49,3739,56],{"class":55},[49,3741,60],{"class":59},[49,3743,3744],{"class":51,"line":63},[49,3745,67],{"emptyLinePlaceholder":66},[49,3747,3748,3750],{"class":51,"line":70},[49,3749,73],{"class":55},[49,3751,77],{"class":76},[49,3753,3754,3756,3758],{"class":51,"line":80},[49,3755,84],{"class":83},[49,3757,87],{"class":59},[49,3759,90],{"class":83},[49,3761,3762,3764,3766],{"class":51,"line":93},[49,3763,84],{"class":83},[49,3765,2891],{"class":59},[49,3767,90],{"class":83},[49,3769,3770],{"class":51,"line":103},[49,3771,106],{"class":76},[49,3773,3774],{"class":51,"line":109},[49,3775,67],{"emptyLinePlaceholder":66},[49,3777,3778,3780,3783,3785,3788,3791,3793,3796,3798],{"class":51,"line":114},[49,3779,117],{"class":55},[49,3781,3782],{"class":59}," squareConcurrent",[49,3784,165],{"class":76},[49,3786,3787],{"class":382},"nums",[49,3789,3790],{"class":76}," []",[49,3792,2138],{"class":55},[49,3794,3795],{"class":76},") []",[49,3797,2138],{"class":55},[49,3799,371],{"class":76},[49,3801,3802,3805,3807,3809,3812,3814,3816,3819],{"class":51,"line":126},[49,3803,3804],{"class":76},"    result ",[49,3806,349],{"class":55},[49,3808,561],{"class":59},[49,3810,3811],{"class":76},"([]",[49,3813,2138],{"class":55},[49,3815,899],{"class":76},[49,3817,3818],{"class":59},"len",[49,3820,3821],{"class":76},"(nums))\n",[49,3823,3824,3827,3829,3831,3833],{"class":51,"line":142},[49,3825,3826],{"class":55},"    var",[49,3828,2888],{"class":76},[49,3830,2891],{"class":59},[49,3832,703],{"class":76},[49,3834,2896],{"class":59},[49,3836,3837],{"class":51,"line":156},[49,3838,67],{"emptyLinePlaceholder":66},[49,3840,3841,3843,3846,3848,3850],{"class":51,"line":173},[49,3842,2497],{"class":55},[49,3844,3845],{"class":76}," i, n ",[49,3847,349],{"class":55},[49,3849,1394],{"class":55},[49,3851,3852],{"class":76}," nums {\n",[49,3854,3855,3858,3860,3862,3864],{"class":51,"line":179},[49,3856,3857],{"class":76},"        wg.",[49,3859,2931],{"class":59},[49,3861,165],{"class":76},[49,3863,2678],{"class":352},[49,3865,106],{"class":76},[49,3867,3868,3871,3873,3875,3878,3880,3883,3885],{"class":51,"line":184},[49,3869,3870],{"class":55},"        go",[49,3872,147],{"class":55},[49,3874,165],{"class":76},[49,3876,3877],{"class":382},"idx",[49,3879,899],{"class":76},[49,3881,3882],{"class":382},"val",[49,3884,386],{"class":55},[49,3886,389],{"class":76},[49,3888,3889,3892,3894,3896],{"class":51,"line":199},[49,3890,3891],{"class":55},"            defer",[49,3893,2962],{"class":76},[49,3895,742],{"class":59},[49,3897,769],{"class":76},[49,3899,3900,3903,3905,3908,3910,3912],{"class":51,"line":205},[49,3901,3902],{"class":76},"            result[idx] ",[49,3904,1333],{"class":55},[49,3906,3907],{"class":76}," val ",[49,3909,2446],{"class":55},[49,3911,3907],{"class":76},[49,3913,3914],{"class":138},"\u002F\u002F каждая горутина пишет в свой индекс\n",[49,3916,3917],{"class":51,"line":210},[49,3918,3919],{"class":76},"        }(i, n)\n",[49,3921,3922],{"class":51,"line":219},[49,3923,2329],{"class":76},[49,3925,3926],{"class":51,"line":234},[49,3927,67],{"emptyLinePlaceholder":66},[49,3929,3930,3932,3934],{"class":51,"line":1603},[49,3931,2928],{"class":76},[49,3933,3003],{"class":59},[49,3935,769],{"class":76},[49,3937,3938,3940],{"class":51,"line":2252},[49,3939,2180],{"class":55},[49,3941,3942],{"class":76}," result\n",[49,3944,3945],{"class":51,"line":2260},[49,3946,202],{"class":76},[49,3948,3949],{"class":51,"line":2275},[49,3950,67],{"emptyLinePlaceholder":66},[49,3952,3953,3955,3957],{"class":51,"line":2290},[49,3954,117],{"class":55},[49,3956,120],{"class":59},[49,3958,123],{"class":76},[49,3960,3961,3963,3965,3967,3970,3972,3974,3977,3979,3981,3983,3985,3987,3989,3992,3994,3996,3999],{"class":51,"line":2312},[49,3962,222],{"class":76},[49,3964,162],{"class":59},[49,3966,165],{"class":76},[49,3968,3969],{"class":59},"squareConcurrent",[49,3971,3811],{"class":76},[49,3973,2138],{"class":55},[49,3975,3976],{"class":76},"{",[49,3978,2678],{"class":352},[49,3980,899],{"class":76},[49,3982,2694],{"class":352},[49,3984,899],{"class":76},[49,3986,1114],{"class":352},[49,3988,899],{"class":76},[49,3990,3991],{"class":352},"4",[49,3993,899],{"class":76},[49,3995,2304],{"class":352},[49,3997,3998],{"class":76},"})) ",[49,4000,4001],{"class":138},"\u002F\u002F [1 4 9 16 25]\n",[49,4003,4004],{"class":51,"line":2326},[49,4005,202],{"class":76},[49,4007,4008],{"class":51,"line":2332},[49,4009,67],{"emptyLinePlaceholder":66},[49,4011,4012],{"class":51,"line":2661},[49,4013,4014],{"class":138},"\u002F\u002F Безопасно без мьютекса: каждая горутина пишет\n",[49,4016,4017],{"class":51,"line":2666},[49,4018,4019],{"class":138},"\u002F\u002F в уникальный индекс — нет гонки данных.\n",[25,4021],{},[16,4023,4024],{},[325,4025,4026],{},"Задача 2: Таймаут операции",[16,4028,4029,4031],{},[325,4030,3695],{}," Средняя",[16,4033,4034,4036],{},[325,4035,3701],{}," select с таймаутом, паттерн ограничения времени",[16,4038,4039,3708,4041,4044,4045,4048,4049,4051,4052,4055,4056,703],{},[325,4040,3707],{},[36,4042,4043],{},"withTimeout(timeout time.Duration, fn func() int) (int, error)"," которая запускает ",[36,4046,4047],{},"fn"," в горутине и возвращает результат. Если ",[36,4050,4047],{}," не завершилась за ",[36,4053,4054],{},"timeout"," — возвращает ошибку ",[36,4057,4058],{},"\"operation timed out\"",[16,4060,4061],{},[325,4062,3717],{},[41,4064,4067],{"className":4065,"code":4066,"language":3723},[3721],"withTimeout(1*time.Second, func() int {\n    time.Sleep(500*time.Millisecond)\n    return 42\n}) → (42, nil)\n\nwithTimeout(100*time.Millisecond, func() int {\n    time.Sleep(1*time.Second)\n    return 42\n}) → (0, \"operation timed out\")\n",[36,4068,4066],{"__ignoreMap":45},[16,4070,4071],{},[325,4072,3730],{},[41,4074,4076],{"className":43,"code":4075,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"errors\"\n    \"fmt\"\n    \"time\"\n)\n\nfunc withTimeout(timeout time.Duration, fn func() int) (int, error) {\n    ch := make(chan int, 1) \u002F\u002F буферизованный — горутина не зависнет\n\n    go func() {\n        ch \u003C- fn()\n    }()\n\n    select {\n    case result := \u003C-ch:\n        return result, nil\n    case \u003C-time.After(timeout):\n        return 0, errors.New(\"operation timed out\")\n    }\n}\n\nfunc main() {\n    result, err := withTimeout(time.Second, func() int {\n        time.Sleep(500 * time.Millisecond)\n        return 42\n    })\n    fmt.Println(result, err) \u002F\u002F 42 \u003Cnil>\n\n    result, err = withTimeout(100*time.Millisecond, func() int {\n        time.Sleep(time.Second)\n        return 42\n    })\n    fmt.Println(result, err) \u002F\u002F 0 operation timed out\n}\n\n\u002F\u002F Буферизованный канал важен: если таймаут сработал раньше\n\u002F\u002F и никто не читает из ch — горутина всё равно запишет\n\u002F\u002F и завершится. Без буфера — утечка горутины.\n",[36,4077,4078,4084,4088,4094,4103,4111,4119,4123,4127,4168,4191,4195,4203,4214,4218,4222,4228,4240,4251,4264,4282,4286,4290,4294,4302,4322,4337,4343,4348,4360,4364,4389,4397,4403,4407,4418,4422,4426,4431,4437],{"__ignoreMap":45},[49,4079,4080,4082],{"class":51,"line":52},[49,4081,56],{"class":55},[49,4083,60],{"class":59},[49,4085,4086],{"class":51,"line":63},[49,4087,67],{"emptyLinePlaceholder":66},[49,4089,4090,4092],{"class":51,"line":70},[49,4091,73],{"class":55},[49,4093,77],{"class":76},[49,4095,4096,4098,4101],{"class":51,"line":80},[49,4097,84],{"class":83},[49,4099,4100],{"class":59},"errors",[49,4102,90],{"class":83},[49,4104,4105,4107,4109],{"class":51,"line":93},[49,4106,84],{"class":83},[49,4108,87],{"class":59},[49,4110,90],{"class":83},[49,4112,4113,4115,4117],{"class":51,"line":103},[49,4114,84],{"class":83},[49,4116,98],{"class":59},[49,4118,90],{"class":83},[49,4120,4121],{"class":51,"line":109},[49,4122,106],{"class":76},[49,4124,4125],{"class":51,"line":114},[49,4126,67],{"emptyLinePlaceholder":66},[49,4128,4129,4131,4134,4136,4138,4141,4143,4146,4148,4150,4152,4154,4156,4159,4161,4163,4166],{"class":51,"line":126},[49,4130,117],{"class":55},[49,4132,4133],{"class":59}," withTimeout",[49,4135,165],{"class":76},[49,4137,4054],{"class":382},[49,4139,4140],{"class":59}," time",[49,4142,703],{"class":76},[49,4144,4145],{"class":59},"Duration",[49,4147,899],{"class":76},[49,4149,4047],{"class":382},[49,4151,147],{"class":55},[49,4153,135],{"class":76},[49,4155,2138],{"class":55},[49,4157,4158],{"class":76},") (",[49,4160,2138],{"class":55},[49,4162,899],{"class":76},[49,4164,4165],{"class":55},"error",[49,4167,389],{"class":76},[49,4169,4170,4172,4174,4176,4178,4180,4182,4184,4186,4188],{"class":51,"line":142},[49,4171,556],{"class":76},[49,4173,349],{"class":55},[49,4175,561],{"class":59},[49,4177,165],{"class":76},[49,4179,566],{"class":55},[49,4181,386],{"class":55},[49,4183,899],{"class":76},[49,4185,2678],{"class":352},[49,4187,2430],{"class":76},[49,4189,4190],{"class":138},"\u002F\u002F буферизованный — горутина не зависнет\n",[49,4192,4193],{"class":51,"line":156},[49,4194,67],{"emptyLinePlaceholder":66},[49,4196,4197,4199,4201],{"class":51,"line":173},[49,4198,129],{"class":55},[49,4200,147],{"class":55},[49,4202,123],{"class":76},[49,4204,4205,4207,4209,4212],{"class":51,"line":179},[49,4206,2235],{"class":76},[49,4208,971],{"class":55},[49,4210,4211],{"class":59}," fn",[49,4213,769],{"class":76},[49,4215,4216],{"class":51,"line":184},[49,4217,176],{"class":76},[49,4219,4220],{"class":51,"line":199},[49,4221,67],{"emptyLinePlaceholder":66},[49,4223,4224,4226],{"class":51,"line":205},[49,4225,2255],{"class":55},[49,4227,371],{"class":76},[49,4229,4230,4232,4234,4236,4238],{"class":51,"line":210},[49,4231,2263],{"class":55},[49,4233,2266],{"class":76},[49,4235,349],{"class":55},[49,4237,588],{"class":55},[49,4239,2034],{"class":76},[49,4241,4242,4245,4248],{"class":51,"line":219},[49,4243,4244],{"class":55},"        return",[49,4246,4247],{"class":76}," result, ",[49,4249,4250],{"class":352},"nil\n",[49,4252,4253,4255,4257,4259,4261],{"class":51,"line":234},[49,4254,2263],{"class":55},[49,4256,588],{"class":55},[49,4258,1004],{"class":76},[49,4260,2299],{"class":59},[49,4262,4263],{"class":76},"(timeout):\n",[49,4265,4266,4268,4270,4273,4276,4278,4280],{"class":51,"line":1603},[49,4267,4244],{"class":55},[49,4269,353],{"class":352},[49,4271,4272],{"class":76},", errors.",[49,4274,4275],{"class":59},"New",[49,4277,165],{"class":76},[49,4279,4058],{"class":83},[49,4281,106],{"class":76},[49,4283,4284],{"class":51,"line":2252},[49,4285,2329],{"class":76},[49,4287,4288],{"class":51,"line":2260},[49,4289,202],{"class":76},[49,4291,4292],{"class":51,"line":2275},[49,4293,67],{"emptyLinePlaceholder":66},[49,4295,4296,4298,4300],{"class":51,"line":2290},[49,4297,117],{"class":55},[49,4299,120],{"class":59},[49,4301,123],{"class":76},[49,4303,4304,4307,4309,4311,4314,4316,4318,4320],{"class":51,"line":2312},[49,4305,4306],{"class":76},"    result, err ",[49,4308,349],{"class":55},[49,4310,4133],{"class":59},[49,4312,4313],{"class":76},"(time.Second, ",[49,4315,117],{"class":55},[49,4317,135],{"class":76},[49,4319,2138],{"class":55},[49,4321,371],{"class":76},[49,4323,4324,4326,4328,4330,4333,4335],{"class":51,"line":2326},[49,4325,481],{"class":76},[49,4327,190],{"class":59},[49,4329,165],{"class":76},[49,4331,4332],{"class":352},"500",[49,4334,491],{"class":55},[49,4336,2175],{"class":76},[49,4338,4339,4341],{"class":51,"line":2332},[49,4340,4244],{"class":55},[49,4342,1485],{"class":352},[49,4344,4345],{"class":51,"line":2661},[49,4346,4347],{"class":76},"    })\n",[49,4349,4350,4352,4354,4357],{"class":51,"line":2666},[49,4351,222],{"class":76},[49,4353,162],{"class":59},[49,4355,4356],{"class":76},"(result, err) ",[49,4358,4359],{"class":138},"\u002F\u002F 42 \u003Cnil>\n",[49,4361,4362],{"class":51,"line":2683},[49,4363,67],{"emptyLinePlaceholder":66},[49,4365,4366,4368,4370,4372,4374,4376,4378,4381,4383,4385,4387],{"class":51,"line":2699},[49,4367,4306],{"class":76},[49,4369,1333],{"class":55},[49,4371,4133],{"class":59},[49,4373,165],{"class":76},[49,4375,2170],{"class":352},[49,4377,2446],{"class":55},[49,4379,4380],{"class":76},"time.Millisecond, ",[49,4382,117],{"class":55},[49,4384,135],{"class":76},[49,4386,2138],{"class":55},[49,4388,371],{"class":76},[49,4390,4391,4393,4395],{"class":51,"line":2714},[49,4392,481],{"class":76},[49,4394,190],{"class":59},[49,4396,1009],{"class":76},[49,4398,4399,4401],{"class":51,"line":2723},[49,4400,4244],{"class":55},[49,4402,1485],{"class":352},[49,4404,4405],{"class":51,"line":2728},[49,4406,4347],{"class":76},[49,4408,4409,4411,4413,4415],{"class":51,"line":2742},[49,4410,222],{"class":76},[49,4412,162],{"class":59},[49,4414,4356],{"class":76},[49,4416,4417],{"class":138},"\u002F\u002F 0 operation timed out\n",[49,4419,4420],{"class":51,"line":2755},[49,4421,202],{"class":76},[49,4423,4424],{"class":51,"line":2768},[49,4425,67],{"emptyLinePlaceholder":66},[49,4427,4428],{"class":51,"line":2776},[49,4429,4430],{"class":138},"\u002F\u002F Буферизованный канал важен: если таймаут сработал раньше\n",[49,4432,4434],{"class":51,"line":4433},39,[49,4435,4436],{"class":138},"\u002F\u002F и никто не читает из ch — горутина всё равно запишет\n",[49,4438,4440],{"class":51,"line":4439},40,[49,4441,4442],{"class":138},"\u002F\u002F и завершится. Без буфера — утечка горутины.\n",[25,4444],{},[16,4446,4447],{},[325,4448,4449],{},"Задача 3: Pipeline с отменой",[16,4451,4452,4454],{},[325,4453,3695],{}," Сложная",[16,4456,4457,4459],{},[325,4458,3701],{}," построение pipeline, корректная отмена через контекст",[16,4461,4462,4464,4465,4468,4469,4472,4473,4476,4477,703],{},[325,4463,3707],{}," Реализуй pipeline из трёх стадий: ",[36,4466,4467],{},"generate"," (числа от 1 до n), ",[36,4470,4471],{},"filter"," (только чётные), ",[36,4474,4475],{},"square"," (возведение в квадрат). Pipeline должен корректно завершаться при отмене контекста. Напиши функцию ",[36,4478,4479],{},"run(ctx context.Context, n int) []int",[16,4481,4482],{},[325,4483,3717],{},[41,4485,4488],{"className":4486,"code":4487,"language":3723},[3721],"run(ctx, 10) → [4 16 36 64 100]  \u002F\u002F чётные 2,4,6,8,10 → квадраты\nrun(ctx, 6)  → [4 16 36]\n",[36,4489,4487],{"__ignoreMap":45},[16,4491,4492,4495,4496,703],{},[325,4493,4494],{},"Подсказка:"," Каждая стадия — отдельная функция возвращающая канал. В каждом select слушай ",[36,4497,4498],{},"ctx.Done()",[16,4500,4501],{},[325,4502,3730],{},[41,4504,4506],{"className":43,"code":4505,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\nfunc generate(ctx context.Context, n int) \u003C-chan int {\n    out := make(chan int)\n    go func() {\n        defer close(out)\n        for i := 1; i \u003C= n; i++ {\n            select {\n            case out \u003C- i:\n            case \u003C-ctx.Done():\n                return\n            }\n        }\n    }()\n    return out\n}\n\nfunc filter(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            if n%2 == 0 {\n                select {\n                case out \u003C- n:\n                case \u003C-ctx.Done():\n                    return\n                }\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 run(ctx context.Context, n int) []int {\n    nums := generate(ctx, n)\n    evens := filter(ctx, nums)\n    squares := square(ctx, evens)\n\n    var result []int\n    for v := range squares {\n        result = append(result, v)\n    }\n    return result\n}\n\nfunc main() {\n    ctx := context.Background()\n    fmt.Println(run(ctx, 10)) \u002F\u002F [4 16 36 64 100]\n    fmt.Println(run(ctx, 6))  \u002F\u002F [4 16 36]\n}\n",[36,4507,4508,4514,4518,4524,4533,4541,4545,4549,4581,4598,4606,4616,4638,4644,4656,4669,4673,4677,4681,4685,4692,4696,4700,4734,4750,4758,4766,4780,4800,4807,4819,4831,4836,4841,4845,4849,4853,4859,4863,4867,4900,4917,4926,4935,4948,4955,4970,4983,4988,4993,4998,5003,5010,5015,5020,5050,5063,5076,5089,5094,5104,5118,5132,5137,5144,5149,5154,5163,5179,5202,5224],{"__ignoreMap":45},[49,4509,4510,4512],{"class":51,"line":52},[49,4511,56],{"class":55},[49,4513,60],{"class":59},[49,4515,4516],{"class":51,"line":63},[49,4517,67],{"emptyLinePlaceholder":66},[49,4519,4520,4522],{"class":51,"line":70},[49,4521,73],{"class":55},[49,4523,77],{"class":76},[49,4525,4526,4528,4531],{"class":51,"line":80},[49,4527,84],{"class":83},[49,4529,4530],{"class":59},"context",[49,4532,90],{"class":83},[49,4534,4535,4537,4539],{"class":51,"line":93},[49,4536,84],{"class":83},[49,4538,87],{"class":59},[49,4540,90],{"class":83},[49,4542,4543],{"class":51,"line":103},[49,4544,106],{"class":76},[49,4546,4547],{"class":51,"line":109},[49,4548,67],{"emptyLinePlaceholder":66},[49,4550,4551,4553,4556,4558,4560,4562,4564,4566,4568,4570,4572,4574,4577,4579],{"class":51,"line":114},[49,4552,117],{"class":55},[49,4554,4555],{"class":59}," generate",[49,4557,165],{"class":76},[49,4559,697],{"class":382},[49,4561,700],{"class":59},[49,4563,703],{"class":76},[49,4565,706],{"class":59},[49,4567,899],{"class":76},[49,4569,383],{"class":382},[49,4571,386],{"class":55},[49,4573,2430],{"class":76},[49,4575,4576],{"class":55},"\u003C-chan",[49,4578,386],{"class":55},[49,4580,371],{"class":76},[49,4582,4583,4586,4588,4590,4592,4594,4596],{"class":51,"line":126},[49,4584,4585],{"class":76},"    out ",[49,4587,349],{"class":55},[49,4589,561],{"class":59},[49,4591,165],{"class":76},[49,4593,566],{"class":55},[49,4595,386],{"class":55},[49,4597,106],{"class":76},[49,4599,4600,4602,4604],{"class":51,"line":142},[49,4601,129],{"class":55},[49,4603,147],{"class":55},[49,4605,123],{"class":76},[49,4607,4608,4610,4613],{"class":51,"line":156},[49,4609,2959],{"class":55},[49,4611,4612],{"class":59}," close",[49,4614,4615],{"class":76},"(out)\n",[49,4617,4618,4620,4622,4624,4626,4628,4631,4634,4636],{"class":51,"line":173},[49,4619,648],{"class":55},[49,4621,346],{"class":76},[49,4623,349],{"class":55},[49,4625,1129],{"class":352},[49,4627,356],{"class":76},[49,4629,4630],{"class":55},"\u003C=",[49,4632,4633],{"class":76}," n; i",[49,4635,368],{"class":55},[49,4637,371],{"class":76},[49,4639,4640,4642],{"class":51,"line":179},[49,4641,727],{"class":55},[49,4643,371],{"class":76},[49,4645,4646,4648,4651,4653],{"class":51,"line":184},[49,4647,734],{"class":55},[49,4649,4650],{"class":76}," out ",[49,4652,971],{"class":55},[49,4654,4655],{"class":76}," i:\n",[49,4657,4658,4660,4662,4664,4666],{"class":51,"line":199},[49,4659,734],{"class":55},[49,4661,588],{"class":55},[49,4663,739],{"class":76},[49,4665,742],{"class":59},[49,4667,4668],{"class":76},"():\n",[49,4670,4671],{"class":51,"line":205},[49,4672,753],{"class":55},[49,4674,4675],{"class":51,"line":210},[49,4676,774],{"class":76},[49,4678,4679],{"class":51,"line":219},[49,4680,665],{"class":76},[49,4682,4683],{"class":51,"line":234},[49,4684,176],{"class":76},[49,4686,4687,4689],{"class":51,"line":1603},[49,4688,2180],{"class":55},[49,4690,4691],{"class":76}," out\n",[49,4693,4694],{"class":51,"line":2252},[49,4695,202],{"class":76},[49,4697,4698],{"class":51,"line":2260},[49,4699,67],{"emptyLinePlaceholder":66},[49,4701,4702,4704,4707,4709,4711,4713,4715,4717,4719,4722,4724,4726,4728,4730,4732],{"class":51,"line":2275},[49,4703,117],{"class":55},[49,4705,4706],{"class":59}," filter",[49,4708,165],{"class":76},[49,4710,697],{"class":382},[49,4712,700],{"class":59},[49,4714,703],{"class":76},[49,4716,706],{"class":59},[49,4718,899],{"class":76},[49,4720,4721],{"class":382},"in",[49,4723,1515],{"class":55},[49,4725,386],{"class":55},[49,4727,2430],{"class":76},[49,4729,4576],{"class":55},[49,4731,386],{"class":55},[49,4733,371],{"class":76},[49,4735,4736,4738,4740,4742,4744,4746,4748],{"class":51,"line":2290},[49,4737,4585],{"class":76},[49,4739,349],{"class":55},[49,4741,561],{"class":59},[49,4743,165],{"class":76},[49,4745,566],{"class":55},[49,4747,386],{"class":55},[49,4749,106],{"class":76},[49,4751,4752,4754,4756],{"class":51,"line":2312},[49,4753,129],{"class":55},[49,4755,147],{"class":55},[49,4757,123],{"class":76},[49,4759,4760,4762,4764],{"class":51,"line":2326},[49,4761,2959],{"class":55},[49,4763,4612],{"class":59},[49,4765,4615],{"class":76},[49,4767,4768,4770,4773,4775,4777],{"class":51,"line":2332},[49,4769,648],{"class":55},[49,4771,4772],{"class":76}," n ",[49,4774,349],{"class":55},[49,4776,1394],{"class":55},[49,4778,4779],{"class":76}," in {\n",[49,4781,4782,4785,4788,4791,4793,4796,4798],{"class":51,"line":2661},[49,4783,4784],{"class":55},"            if",[49,4786,4787],{"class":76}," n",[49,4789,4790],{"class":55},"%",[49,4792,2694],{"class":352},[49,4794,4795],{"class":55}," ==",[49,4797,353],{"class":352},[49,4799,371],{"class":76},[49,4801,4802,4805],{"class":51,"line":2666},[49,4803,4804],{"class":55},"                select",[49,4806,371],{"class":76},[49,4808,4809,4812,4814,4816],{"class":51,"line":2683},[49,4810,4811],{"class":55},"                case",[49,4813,4650],{"class":76},[49,4815,971],{"class":55},[49,4817,4818],{"class":76}," n:\n",[49,4820,4821,4823,4825,4827,4829],{"class":51,"line":2699},[49,4822,4811],{"class":55},[49,4824,588],{"class":55},[49,4826,739],{"class":76},[49,4828,742],{"class":59},[49,4830,4668],{"class":76},[49,4832,4833],{"class":51,"line":2714},[49,4834,4835],{"class":55},"                    return\n",[49,4837,4838],{"class":51,"line":2723},[49,4839,4840],{"class":76},"                }\n",[49,4842,4843],{"class":51,"line":2728},[49,4844,774],{"class":76},[49,4846,4847],{"class":51,"line":2742},[49,4848,665],{"class":76},[49,4850,4851],{"class":51,"line":2755},[49,4852,176],{"class":76},[49,4854,4855,4857],{"class":51,"line":2768},[49,4856,2180],{"class":55},[49,4858,4691],{"class":76},[49,4860,4861],{"class":51,"line":2776},[49,4862,202],{"class":76},[49,4864,4865],{"class":51,"line":4433},[49,4866,67],{"emptyLinePlaceholder":66},[49,4868,4869,4871,4874,4876,4878,4880,4882,4884,4886,4888,4890,4892,4894,4896,4898],{"class":51,"line":4439},[49,4870,117],{"class":55},[49,4872,4873],{"class":59}," square",[49,4875,165],{"class":76},[49,4877,697],{"class":382},[49,4879,700],{"class":59},[49,4881,703],{"class":76},[49,4883,706],{"class":59},[49,4885,899],{"class":76},[49,4887,4721],{"class":382},[49,4889,1515],{"class":55},[49,4891,386],{"class":55},[49,4893,2430],{"class":76},[49,4895,4576],{"class":55},[49,4897,386],{"class":55},[49,4899,371],{"class":76},[49,4901,4903,4905,4907,4909,4911,4913,4915],{"class":51,"line":4902},41,[49,4904,4585],{"class":76},[49,4906,349],{"class":55},[49,4908,561],{"class":59},[49,4910,165],{"class":76},[49,4912,566],{"class":55},[49,4914,386],{"class":55},[49,4916,106],{"class":76},[49,4918,4920,4922,4924],{"class":51,"line":4919},42,[49,4921,129],{"class":55},[49,4923,147],{"class":55},[49,4925,123],{"class":76},[49,4927,4929,4931,4933],{"class":51,"line":4928},43,[49,4930,2959],{"class":55},[49,4932,4612],{"class":59},[49,4934,4615],{"class":76},[49,4936,4938,4940,4942,4944,4946],{"class":51,"line":4937},44,[49,4939,648],{"class":55},[49,4941,4772],{"class":76},[49,4943,349],{"class":55},[49,4945,1394],{"class":55},[49,4947,4779],{"class":76},[49,4949,4951,4953],{"class":51,"line":4950},45,[49,4952,727],{"class":55},[49,4954,371],{"class":76},[49,4956,4958,4960,4962,4964,4966,4968],{"class":51,"line":4957},46,[49,4959,734],{"class":55},[49,4961,4650],{"class":76},[49,4963,971],{"class":55},[49,4965,4772],{"class":76},[49,4967,2446],{"class":55},[49,4969,4818],{"class":76},[49,4971,4973,4975,4977,4979,4981],{"class":51,"line":4972},47,[49,4974,734],{"class":55},[49,4976,588],{"class":55},[49,4978,739],{"class":76},[49,4980,742],{"class":59},[49,4982,4668],{"class":76},[49,4984,4986],{"class":51,"line":4985},48,[49,4987,753],{"class":55},[49,4989,4991],{"class":51,"line":4990},49,[49,4992,774],{"class":76},[49,4994,4996],{"class":51,"line":4995},50,[49,4997,665],{"class":76},[49,4999,5001],{"class":51,"line":5000},51,[49,5002,176],{"class":76},[49,5004,5006,5008],{"class":51,"line":5005},52,[49,5007,2180],{"class":55},[49,5009,4691],{"class":76},[49,5011,5013],{"class":51,"line":5012},53,[49,5014,202],{"class":76},[49,5016,5018],{"class":51,"line":5017},54,[49,5019,67],{"emptyLinePlaceholder":66},[49,5021,5023,5025,5028,5030,5032,5034,5036,5038,5040,5042,5044,5046,5048],{"class":51,"line":5022},55,[49,5024,117],{"class":55},[49,5026,5027],{"class":59}," run",[49,5029,165],{"class":76},[49,5031,697],{"class":382},[49,5033,700],{"class":59},[49,5035,703],{"class":76},[49,5037,706],{"class":59},[49,5039,899],{"class":76},[49,5041,383],{"class":382},[49,5043,386],{"class":55},[49,5045,3795],{"class":76},[49,5047,2138],{"class":55},[49,5049,371],{"class":76},[49,5051,5053,5056,5058,5060],{"class":51,"line":5052},56,[49,5054,5055],{"class":76},"    nums ",[49,5057,349],{"class":55},[49,5059,4555],{"class":59},[49,5061,5062],{"class":76},"(ctx, n)\n",[49,5064,5066,5069,5071,5073],{"class":51,"line":5065},57,[49,5067,5068],{"class":76},"    evens ",[49,5070,349],{"class":55},[49,5072,4706],{"class":59},[49,5074,5075],{"class":76},"(ctx, nums)\n",[49,5077,5079,5082,5084,5086],{"class":51,"line":5078},58,[49,5080,5081],{"class":76},"    squares ",[49,5083,349],{"class":55},[49,5085,4873],{"class":59},[49,5087,5088],{"class":76},"(ctx, evens)\n",[49,5090,5092],{"class":51,"line":5091},59,[49,5093,67],{"emptyLinePlaceholder":66},[49,5095,5097,5099,5102],{"class":51,"line":5096},60,[49,5098,3826],{"class":55},[49,5100,5101],{"class":76}," result []",[49,5103,3244],{"class":55},[49,5105,5107,5109,5111,5113,5115],{"class":51,"line":5106},61,[49,5108,2497],{"class":55},[49,5110,1389],{"class":76},[49,5112,349],{"class":55},[49,5114,1394],{"class":55},[49,5116,5117],{"class":76}," squares {\n",[49,5119,5121,5124,5126,5129],{"class":51,"line":5120},62,[49,5122,5123],{"class":76},"        result ",[49,5125,1333],{"class":55},[49,5127,5128],{"class":59}," append",[49,5130,5131],{"class":76},"(result, v)\n",[49,5133,5135],{"class":51,"line":5134},63,[49,5136,2329],{"class":76},[49,5138,5140,5142],{"class":51,"line":5139},64,[49,5141,2180],{"class":55},[49,5143,3942],{"class":76},[49,5145,5147],{"class":51,"line":5146},65,[49,5148,202],{"class":76},[49,5150,5152],{"class":51,"line":5151},66,[49,5153,67],{"emptyLinePlaceholder":66},[49,5155,5157,5159,5161],{"class":51,"line":5156},67,[49,5158,117],{"class":55},[49,5160,120],{"class":59},[49,5162,123],{"class":76},[49,5164,5166,5169,5171,5174,5177],{"class":51,"line":5165},68,[49,5167,5168],{"class":76},"    ctx ",[49,5170,349],{"class":55},[49,5172,5173],{"class":76}," context.",[49,5175,5176],{"class":59},"Background",[49,5178,769],{"class":76},[49,5180,5182,5184,5186,5188,5191,5194,5196,5199],{"class":51,"line":5181},69,[49,5183,222],{"class":76},[49,5185,162],{"class":59},[49,5187,165],{"class":76},[49,5189,5190],{"class":59},"run",[49,5192,5193],{"class":76},"(ctx, ",[49,5195,488],{"class":352},[49,5197,5198],{"class":76},")) ",[49,5200,5201],{"class":138},"\u002F\u002F [4 16 36 64 100]\n",[49,5203,5205,5207,5209,5211,5213,5215,5218,5221],{"class":51,"line":5204},70,[49,5206,222],{"class":76},[49,5208,162],{"class":59},[49,5210,165],{"class":76},[49,5212,5190],{"class":59},[49,5214,5193],{"class":76},[49,5216,5217],{"class":352},"6",[49,5219,5220],{"class":76},"))  ",[49,5222,5223],{"class":138},"\u002F\u002F [4 16 36]\n",[49,5225,5227],{"class":51,"line":5226},71,[49,5228,202],{"class":76},[25,5230],{},[28,5232,5234],{"id":5233},"практика","Практика",[5236,5237,5239,5242,5259],"quiz",{"answer":2694,"id":5238,"xp":488},"concurrency-goroutines-q1",[16,5240,5241],{},"Чем небуферизованный канал отличается от буферизованного?",[5243,5244,5245],"template",{"v-slot:options":45},[1228,5246,5247,5250,5253,5256],{},[1231,5248,5249],{},"Небуферизованный работает быстрее — нет накладных расходов на буфер",[1231,5251,5252],{},"Небуферизованный блокирует отправителя до тех пор, пока получатель не готов принять (рандеву)",[1231,5254,5255],{},"Буферизованный гарантирует порядок доставки, небуферизованный — нет",[1231,5257,5258],{},"Разницы нет — оба работают одинаково",[5243,5260,5261],{"v-slot:explanation":45},[16,5262,5263,5264,5267,5268,5271,5272,5275],{},"Небуферизованный канал (",[36,5265,5266],{},"make(chan T)",") — это ",[325,5269,5270],{},"рандеву",": отправка блокируется пока получатель не готов, и наоборот. Буферизованный (",[36,5273,5274],{},"make(chan T, n)",") — асинхронная очередь FIFO: отправка не блокируется пока буфер не заполнен. Небуферизованный используется для строгой синхронизации, буферизованный — для сглаживания пиков.",[5277,5278,5282,5285,5494],"predict",{"answer":5279,"id":5280,"xp":5281},"отправляю...\\nполучаю...\\nотправил\\nполучил: 42","concurrency-goroutines-p1","15",[16,5283,5284],{},"Угадай точный вывод программы. Обрати внимание на порядок строк.",[5243,5286,5287],{"v-slot:code":45},[41,5288,5290],{"className":43,"code":5289,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\nfunc main() {\n    ch := make(chan int)\n    done := make(chan struct{})\n\n    go func() {\n        defer close(done)\n        fmt.Println(\"отправляю...\")\n        ch \u003C- 42\n        fmt.Println(\"отправил\")\n    }()\n\n    time.Sleep(50 * time.Millisecond)\n    fmt.Println(\"получаю...\")\n    val := \u003C-ch\n    \u003C-done\n    fmt.Println(\"получил:\", val)\n}\n",[36,5291,5292,5298,5302,5308,5316,5324,5328,5332,5340,5356,5373,5377,5385,5394,5406,5414,5426,5430,5434,5449,5461,5471,5478,5490],{"__ignoreMap":45},[49,5293,5294,5296],{"class":51,"line":52},[49,5295,56],{"class":55},[49,5297,60],{"class":59},[49,5299,5300],{"class":51,"line":63},[49,5301,67],{"emptyLinePlaceholder":66},[49,5303,5304,5306],{"class":51,"line":70},[49,5305,73],{"class":55},[49,5307,77],{"class":76},[49,5309,5310,5312,5314],{"class":51,"line":80},[49,5311,84],{"class":83},[49,5313,87],{"class":59},[49,5315,90],{"class":83},[49,5317,5318,5320,5322],{"class":51,"line":93},[49,5319,84],{"class":83},[49,5321,98],{"class":59},[49,5323,90],{"class":83},[49,5325,5326],{"class":51,"line":103},[49,5327,106],{"class":76},[49,5329,5330],{"class":51,"line":109},[49,5331,67],{"emptyLinePlaceholder":66},[49,5333,5334,5336,5338],{"class":51,"line":114},[49,5335,117],{"class":55},[49,5337,120],{"class":59},[49,5339,123],{"class":76},[49,5341,5342,5344,5346,5348,5350,5352,5354],{"class":51,"line":126},[49,5343,556],{"class":76},[49,5345,349],{"class":55},[49,5347,561],{"class":59},[49,5349,165],{"class":76},[49,5351,566],{"class":55},[49,5353,386],{"class":55},[49,5355,106],{"class":76},[49,5357,5358,5361,5363,5365,5367,5369,5371],{"class":51,"line":142},[49,5359,5360],{"class":76},"    done ",[49,5362,349],{"class":55},[49,5364,561],{"class":59},[49,5366,165],{"class":76},[49,5368,566],{"class":55},[49,5370,1638],{"class":55},[49,5372,2645],{"class":76},[49,5374,5375],{"class":51,"line":156},[49,5376,67],{"emptyLinePlaceholder":66},[49,5378,5379,5381,5383],{"class":51,"line":173},[49,5380,129],{"class":55},[49,5382,147],{"class":55},[49,5384,123],{"class":76},[49,5386,5387,5389,5391],{"class":51,"line":179},[49,5388,2959],{"class":55},[49,5390,4612],{"class":59},[49,5392,5393],{"class":76},"(done)\n",[49,5395,5396,5398,5400,5402,5404],{"class":51,"line":184},[49,5397,159],{"class":76},[49,5399,162],{"class":59},[49,5401,165],{"class":76},[49,5403,962],{"class":83},[49,5405,106],{"class":76},[49,5407,5408,5410,5412],{"class":51,"line":199},[49,5409,2235],{"class":76},[49,5411,971],{"class":55},[49,5413,1485],{"class":352},[49,5415,5416,5418,5420,5422,5424],{"class":51,"line":205},[49,5417,159],{"class":76},[49,5419,162],{"class":59},[49,5421,165],{"class":76},[49,5423,988],{"class":83},[49,5425,106],{"class":76},[49,5427,5428],{"class":51,"line":210},[49,5429,176],{"class":76},[49,5431,5432],{"class":51,"line":219},[49,5433,67],{"emptyLinePlaceholder":66},[49,5435,5436,5438,5440,5442,5445,5447],{"class":51,"line":234},[49,5437,187],{"class":76},[49,5439,190],{"class":59},[49,5441,165],{"class":76},[49,5443,5444],{"class":352},"50",[49,5446,491],{"class":55},[49,5448,2175],{"class":76},[49,5450,5451,5453,5455,5457,5459],{"class":51,"line":1603},[49,5452,222],{"class":76},[49,5454,162],{"class":59},[49,5456,165],{"class":76},[49,5458,1021],{"class":83},[49,5460,106],{"class":76},[49,5462,5463,5465,5467,5469],{"class":51,"line":2252},[49,5464,1527],{"class":76},[49,5466,349],{"class":55},[49,5468,588],{"class":55},[49,5470,1310],{"class":76},[49,5472,5473,5475],{"class":51,"line":2260},[49,5474,3152],{"class":55},[49,5476,5477],{"class":76},"done\n",[49,5479,5480,5482,5484,5486,5488],{"class":51,"line":2275},[49,5481,222],{"class":76},[49,5483,162],{"class":59},[49,5485,165],{"class":76},[49,5487,1048],{"class":83},[49,5489,1051],{"class":76},[49,5491,5492],{"class":51,"line":2290},[49,5493,202],{"class":76},[5243,5495,5496],{"v-slot:hint":45},[16,5497,5498,5499,5502,5503,5506],{},"Горутина запускается и сразу печатает \"отправляю...\", затем блокируется на ",[36,5500,5501],{},"ch \u003C- 42",". Main спит 50мс, печатает \"получаю...\", читает из канала — разблокируя горутину. После чтения main ждёт ",[36,5504,5505],{},"done",", поэтому строка \"отправил\" гарантированно появится до \"получил: 42\".",[5508,5509,5513,5516,5601],"code-fix",{"expected":5510,"id":5511,"xp":5512},"горутина завершилась\\nвсё готово","concurrency-goroutines-cf1","25",[16,5514,5515],{},"Исправь код: программа должна дождаться завершения горутины перед выводом \"всё готово\". Сейчас \"всё готово\" может напечататься до горутины или горутина может не выполниться вообще.",[5243,5517,5518],{"v-slot:code":45},[41,5519,5521],{"className":43,"code":5520,"language":38,"meta":45,"style":45},"package main\n\nimport \"fmt\"\n\nfunc main() {\n    go func() {\n        fmt.Println(\"горутина завершилась\")\n    }()\n\n    fmt.Println(\"всё готово\")\n}\n",[36,5522,5523,5529,5533,5543,5547,5555,5563,5576,5580,5584,5597],{"__ignoreMap":45},[49,5524,5525,5527],{"class":51,"line":52},[49,5526,56],{"class":55},[49,5528,60],{"class":59},[49,5530,5531],{"class":51,"line":63},[49,5532,67],{"emptyLinePlaceholder":66},[49,5534,5535,5537,5539,5541],{"class":51,"line":70},[49,5536,73],{"class":55},[49,5538,1445],{"class":83},[49,5540,87],{"class":59},[49,5542,90],{"class":83},[49,5544,5545],{"class":51,"line":80},[49,5546,67],{"emptyLinePlaceholder":66},[49,5548,5549,5551,5553],{"class":51,"line":93},[49,5550,117],{"class":55},[49,5552,120],{"class":59},[49,5554,123],{"class":76},[49,5556,5557,5559,5561],{"class":51,"line":103},[49,5558,129],{"class":55},[49,5560,147],{"class":55},[49,5562,123],{"class":76},[49,5564,5565,5567,5569,5571,5574],{"class":51,"line":109},[49,5566,159],{"class":76},[49,5568,162],{"class":59},[49,5570,165],{"class":76},[49,5572,5573],{"class":83},"\"горутина завершилась\"",[49,5575,106],{"class":76},[49,5577,5578],{"class":51,"line":114},[49,5579,176],{"class":76},[49,5581,5582],{"class":51,"line":126},[49,5583,67],{"emptyLinePlaceholder":66},[49,5585,5586,5588,5590,5592,5595],{"class":51,"line":142},[49,5587,222],{"class":76},[49,5589,162],{"class":59},[49,5591,165],{"class":76},[49,5593,5594],{"class":83},"\"всё готово\"",[49,5596,106],{"class":76},[49,5598,5599],{"class":51,"line":156},[49,5600,202],{"class":76},[5243,5602,5603],{"v-slot:hints":45},[1228,5604,5605,5611,5621,5628],{},[1231,5606,5607,5608],{},"Создай канал-сигнал: ",[36,5609,5610],{},"done := make(chan struct{})",[1231,5612,5613,5614,5617,5618],{},"В горутине после работы закрой канал: ",[36,5615,5616],{},"close(done)"," или отправь значение: ",[36,5619,5620],{},"done \u003C- struct{}{}",[1231,5622,5623,5624,5627],{},"В main прочитай из канала: ",[36,5625,5626],{},"\u003C-done"," — это заблокирует выполнение до получения сигнала",[1231,5629,5630,5631],{},"Альтернатива: используй ",[36,5632,245],{},[5634,5635,5639,5652,5883],"code-task",{"expected":5636,"id":5637,"xp":5638},"1\\n4\\n9\\n16\\n25","concurrency-goroutines-ct1","20",[16,5640,5641,5642,5645,5646,5649,5650,703],{},"Реализуй функцию ",[36,5643,5644],{},"mapConcurrent",", которая применяет функцию ",[36,5647,5648],{},"f"," к каждому элементу слайса конкурентно (каждый элемент в отдельной горутине) и возвращает результаты в том же порядке. Используй ",[36,5651,245],{},[5243,5653,5654],{"v-slot:template":45},[41,5655,5657],{"className":43,"code":5656,"language":38,"meta":45,"style":45},"package main\n\nimport (\n    \"fmt\"\n    \"sync\"\n)\n\nfunc mapConcurrent(nums []int, f func(int) int) []int {\n    var wg sync.WaitGroup\n    _ = wg\n    return nil\n}\n\nfunc main() {\n    result := mapConcurrent(\n        []int{1, 2, 3, 4, 5},\n        func(x int) int { return x * x },\n    )\n    for _, v := range result {\n        fmt.Println(v)\n    }\n}\n",[36,5658,5659,5665,5669,5675,5683,5691,5695,5699,5734,5746,5756,5763,5767,5771,5779,5790,5820,5848,5853,5867,5875,5879],{"__ignoreMap":45},[49,5660,5661,5663],{"class":51,"line":52},[49,5662,56],{"class":55},[49,5664,60],{"class":59},[49,5666,5667],{"class":51,"line":63},[49,5668,67],{"emptyLinePlaceholder":66},[49,5670,5671,5673],{"class":51,"line":70},[49,5672,73],{"class":55},[49,5674,77],{"class":76},[49,5676,5677,5679,5681],{"class":51,"line":80},[49,5678,84],{"class":83},[49,5680,87],{"class":59},[49,5682,90],{"class":83},[49,5684,5685,5687,5689],{"class":51,"line":93},[49,5686,84],{"class":83},[49,5688,2891],{"class":59},[49,5690,90],{"class":83},[49,5692,5693],{"class":51,"line":103},[49,5694,106],{"class":76},[49,5696,5697],{"class":51,"line":109},[49,5698,67],{"emptyLinePlaceholder":66},[49,5700,5701,5703,5706,5708,5710,5712,5714,5716,5718,5720,5722,5724,5726,5728,5730,5732],{"class":51,"line":114},[49,5702,117],{"class":55},[49,5704,5705],{"class":59}," mapConcurrent",[49,5707,165],{"class":76},[49,5709,3787],{"class":382},[49,5711,3790],{"class":76},[49,5713,2138],{"class":55},[49,5715,899],{"class":76},[49,5717,5648],{"class":382},[49,5719,147],{"class":55},[49,5721,165],{"class":76},[49,5723,2138],{"class":55},[49,5725,2430],{"class":76},[49,5727,2138],{"class":55},[49,5729,3795],{"class":76},[49,5731,2138],{"class":55},[49,5733,371],{"class":76},[49,5735,5736,5738,5740,5742,5744],{"class":51,"line":126},[49,5737,3826],{"class":55},[49,5739,2888],{"class":76},[49,5741,2891],{"class":59},[49,5743,703],{"class":76},[49,5745,2896],{"class":59},[49,5747,5748,5751,5753],{"class":51,"line":142},[49,5749,5750],{"class":76},"    _ ",[49,5752,1333],{"class":55},[49,5754,5755],{"class":76}," wg\n",[49,5757,5758,5760],{"class":51,"line":156},[49,5759,2180],{"class":55},[49,5761,5762],{"class":352}," nil\n",[49,5764,5765],{"class":51,"line":173},[49,5766,202],{"class":76},[49,5768,5769],{"class":51,"line":179},[49,5770,67],{"emptyLinePlaceholder":66},[49,5772,5773,5775,5777],{"class":51,"line":184},[49,5774,117],{"class":55},[49,5776,120],{"class":59},[49,5778,123],{"class":76},[49,5780,5781,5783,5785,5787],{"class":51,"line":199},[49,5782,3804],{"class":76},[49,5784,349],{"class":55},[49,5786,5705],{"class":59},[49,5788,5789],{"class":76},"(\n",[49,5791,5792,5795,5797,5799,5801,5803,5805,5807,5809,5811,5813,5815,5817],{"class":51,"line":205},[49,5793,5794],{"class":76},"        []",[49,5796,2138],{"class":55},[49,5798,3976],{"class":76},[49,5800,2678],{"class":352},[49,5802,899],{"class":76},[49,5804,2694],{"class":352},[49,5806,899],{"class":76},[49,5808,1114],{"class":352},[49,5810,899],{"class":76},[49,5812,3991],{"class":352},[49,5814,899],{"class":76},[49,5816,2304],{"class":352},[49,5818,5819],{"class":76},"},\n",[49,5821,5822,5825,5827,5830,5832,5834,5836,5838,5840,5843,5845],{"class":51,"line":210},[49,5823,5824],{"class":55},"        func",[49,5826,165],{"class":76},[49,5828,5829],{"class":382},"x",[49,5831,386],{"class":55},[49,5833,2430],{"class":76},[49,5835,2138],{"class":55},[49,5837,2435],{"class":76},[49,5839,2438],{"class":55},[49,5841,5842],{"class":76}," x ",[49,5844,2446],{"class":55},[49,5846,5847],{"class":76}," x },\n",[49,5849,5850],{"class":51,"line":219},[49,5851,5852],{"class":76},"    )\n",[49,5854,5855,5857,5860,5862,5864],{"class":51,"line":234},[49,5856,2497],{"class":55},[49,5858,5859],{"class":76}," _, v ",[49,5861,349],{"class":55},[49,5863,1394],{"class":55},[49,5865,5866],{"class":76}," result {\n",[49,5868,5869,5871,5873],{"class":51,"line":1603},[49,5870,159],{"class":76},[49,5872,162],{"class":59},[49,5874,1406],{"class":76},[49,5876,5877],{"class":51,"line":2252},[49,5878,2329],{"class":76},[49,5880,5881],{"class":51,"line":2260},[49,5882,202],{"class":76},[5243,5884,5885],{"v-slot:hints":45},[1228,5886,5887,5894,5905,5915],{},[1231,5888,5889,5890,5893],{},"Создай ",[36,5891,5892],{},"result := make([]int, len(nums))"," — каждая горутина пишет в свой индекс",[1231,5895,5896,5897,899,5900,3615,5902,5904],{},"Используй ",[36,5898,5899],{},"var wg sync.WaitGroup",[36,5901,3013],{},[36,5903,3618],{}," внутри",[1231,5906,5907,5908,5911,5912,5914],{},"Передавай ",[36,5909,5910],{},"i"," и ",[36,5913,383],{}," как аргументы горутины, чтобы избежать захвата переменной цикла",[1231,5916,5917,5918,5920,5921],{},"В конце вызови ",[36,5919,3020],{}," перед ",[36,5922,5923],{},"return result",[5925,5926,5927],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":45,"searchDepth":63,"depth":63,"links":5929},[5930,5935,5941,5942,5947,5951,5952,5957,5958],{"id":30,"depth":63,"text":31,"children":5931},[5932,5933,5934],{"id":250,"depth":70,"text":251},{"id":406,"depth":70,"text":407},{"id":523,"depth":70,"text":524},{"id":850,"depth":63,"text":851,"children":5936},[5937,5938,5939,5940],{"id":910,"depth":70,"text":911},{"id":1086,"depth":70,"text":1087},{"id":1218,"depth":70,"text":1219},{"id":1419,"depth":70,"text":1420},{"id":1610,"depth":63,"text":1611},{"id":1823,"depth":63,"text":1824,"children":5943},[5944,5945,5946],{"id":1983,"depth":70,"text":1984},{"id":2074,"depth":70,"text":2075},{"id":2346,"depth":70,"text":2347},{"id":2783,"depth":63,"text":2784,"children":5948},[5949,5950],{"id":2790,"depth":70,"text":2791},{"id":2868,"depth":70,"text":2869},{"id":3028,"depth":63,"text":3029},{"id":3215,"depth":63,"text":3216,"children":5953},[5954,5955,5956],{"id":3219,"depth":70,"text":3220},{"id":3376,"depth":70,"text":3377},{"id":3418,"depth":70,"text":3419},{"id":3510,"depth":63,"text":3511},{"id":5233,"depth":63,"text":5234},1782749315485]