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