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