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