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