[{"data":1,"prerenderedAt":3865},["ShallowReactive",2],{"content:\u002F03-concurrency\u002F06-lock-free":3},{"title":4,"description":5,"path":6,"body":7},"Lock-free и алгоритмы синхронизации","","\u002F03-concurrency\u002F06-lock-free",{"type":8,"value":9,"toc":3801},"minimark",[10,23,26,31,36,46,59,63,66,72,76,82,88,94,97,103,105,116,118,122,126,129,133,303,307,464,468,475,479,486,489,491,506,508,512,516,519,526,530,716,719,722,983,989,992,1230,1236,1240,1243,1260,1263,1265,1276,1278,1282,1286,1498,1502,1508,1514,1520,1530,1534,1540,1546,1550,1561,1563,1574,1576,1580,1584,1590,1608,1612,1615,1621,1624,1628,1634,1638,1848,1851,1857,1861,1866,1871,1877,1879,1890,1892,1896,1900,1951,1954,1958,2316,2320,2323,2329,2333,2347,2351,2354,2358,2372,2374,2385,2387,2391,2394,2397,2402,2512,2517,2706,2710,2716,2723,2726,2730,2741,2747,2749,2760,2762,2766,2770,2773,2779,2782,2786,2825,2828,2832,2839,2845,2852,2858,2864,2868,2875,2878,2884,3013,3017,3024,3030,3113,3119,3129,3133,3139,3143,3154,3156,3167,3169,3173,3177,3183,3186,3190,3196,3200,3206,3210,3213,3219,3222,3236,3243,3247,3250,3252,3256,3290,3464,3797],[11,12,13,17,20],"ul",{},[14,15,16],"li",{},"ABA-проблема — CAS видит тот же адрес, но данные за ним уже другие. Аллокатор переиспользовал освобождённую память",[14,18,19],{},"В Go не возникает: GC не освободит память пока есть ссылка. Актуально для C\u002FC++ и кастомных аллокаторов",[14,21,22],{},"Решения: hazard pointers, tagged pointers, GC",[24,25],"hr",{},[27,28,30],"h2",{"id":29},"aba-и-cas","ABA и CAS",[32,33,35],"h3",{"id":34},"суть-проблемы","Суть проблемы",[37,38,43],"pre",{"className":39,"code":41,"language":42},[40],"language-text","Горутина 1: загрузила head → указывает на элемент A (адрес 0x0A)\n            ... засыпает ...\n\nГорутина 2: Pop(A), Pop(B), Push(C)\n            → аллокатор выделил C по тому же адресу 0x0A (переиспользование)\n\nГорутина 1: просыпается\n            CAS(&head, 0x0A, ...) → УСПЕХ! адрес совпал\n            → но там уже элемент C, не A. Стек сломан.\n","text",[44,45,41],"code",{"__ignoreMap":5},[47,48,49,50,54,55,58],"p",{},"CAS сравнивает ",[51,52,53],"strong",{},"адреса",", не ",[51,56,57],{},"содержимое",". Адрес 0x0A = 0x0A, хотя за ним совершенно другие данные.",[32,60,62],{"id":61},"почему-в-go-не-возникает","Почему в Go не возникает",[47,64,65],{},"GC не освободит объект, пока на него есть хоть одна ссылка. Горутина 1 держит указатель на A → A не будет собран → аллокатор не переиспользует этот адрес → CAS корректно обнаружит изменение.",[47,67,68,71],{},[51,69,70],{},"Возникает если",": пишете кастомный аллокатор на Go, или работаете в C\u002FC++ где free() возвращает память немедленно.",[32,73,75],{"id":74},"решения","Решения",[47,77,78,81],{},[51,79,80],{},"Hazard Pointers"," — список \"опасных\" указателей. Если указатель в списке — нельзя освобождать память. Перед обращением — добавляешь в список, после — убираешь.",[47,83,84,87],{},[51,85,86],{},"Tagged Pointers"," — в неиспользуемых битах адреса (например, верхние 16 из 64) хранится счётчик. При каждом обращении счётчик инкрементируется. CAS сравнивает адрес + счётчик → даже если адрес совпал, счётчик не совпадёт.",[37,89,92],{"className":90,"code":91,"language":42},[40],"Адрес без тега:  0x000000000000_0A\nАдрес с тегом:   0x0001_0000000000_0A  (тег=1)\nПосле операции:  0x0002_0000000000_0A  (тег=2) → CAS не пройдёт\n",[44,93,91],{"__ignoreMap":5},[47,95,96],{},"Используется в Boost (C++). Зависит от архитектуры (сколько бит доступно).",[47,98,99,102],{},[51,100,101],{},"GC"," — встроенный (как в Go) или локальный для структуры данных. Некоторые реализации пишут мини-GC специально для одной структуры.",[24,104],{},[11,106,107,110,113],{},[14,108,109],{},"Стек Трайбера — lock-free стек на связанном списке. Push и Pop = один CAS-луп на голове",[14,111,112],{},"Высокий contention: все горутины оперируют одной точкой (головой) → не лучшая структура для конкурентного доступа",[14,114,115],{},"Подвержен ABA-проблеме (в Go защищает GC)",[24,117],{},[27,119,121],{"id":120},"lock-free-stack","Lock-free stack",[32,123,125],{"id":124},"идея","Идея",[47,127,128],{},"Стек = связанный список, операции только с головой. CAS атомарно обновляет указатель на голову.",[32,130,132],{"id":131},"push","Push",[37,134,138],{"className":135,"code":136,"language":137,"meta":5,"style":5},"language-go shiki shiki-themes github-dark","func (s *Stack) Push(val int) {\n    node := &Item{value: val}\n    for {\n        head := atomic.LoadPointer(&s.head)\n        node.next = head                              \u002F\u002F next → старая голова\n        if atomic.CompareAndSwapPointer(&s.head, head, unsafe.Pointer(node)) {\n            return  \u002F\u002F голова обновлена на новый элемент\n        }\n        \u002F\u002F кто-то встроился → повторяем\n    }\n}\n","go",[44,139,140,181,199,208,230,246,270,279,285,291,297],{"__ignoreMap":5},[141,142,145,149,153,157,160,164,167,169,172,175,178],"span",{"class":143,"line":144},"line",1,[141,146,148],{"class":147},"snl16","func",[141,150,152],{"class":151},"s95oV"," (",[141,154,156],{"class":155},"s9osk","s ",[141,158,159],{"class":147},"*",[141,161,163],{"class":162},"svObZ","Stack",[141,165,166],{"class":151},") ",[141,168,132],{"class":162},[141,170,171],{"class":151},"(",[141,173,174],{"class":155},"val",[141,176,177],{"class":147}," int",[141,179,180],{"class":151},") {\n",[141,182,184,187,190,193,196],{"class":143,"line":183},2,[141,185,186],{"class":151},"    node ",[141,188,189],{"class":147},":=",[141,191,192],{"class":147}," &",[141,194,195],{"class":162},"Item",[141,197,198],{"class":151},"{value: val}\n",[141,200,202,205],{"class":143,"line":201},3,[141,203,204],{"class":147},"    for",[141,206,207],{"class":151}," {\n",[141,209,211,214,216,219,222,224,227],{"class":143,"line":210},4,[141,212,213],{"class":151},"        head ",[141,215,189],{"class":147},[141,217,218],{"class":151}," atomic.",[141,220,221],{"class":162},"LoadPointer",[141,223,171],{"class":151},[141,225,226],{"class":147},"&",[141,228,229],{"class":151},"s.head)\n",[141,231,233,236,239,242],{"class":143,"line":232},5,[141,234,235],{"class":151},"        node.next ",[141,237,238],{"class":147},"=",[141,240,241],{"class":151}," head                              ",[141,243,245],{"class":244},"sAwPA","\u002F\u002F next → старая голова\n",[141,247,249,252,254,257,259,261,264,267],{"class":143,"line":248},6,[141,250,251],{"class":147},"        if",[141,253,218],{"class":151},[141,255,256],{"class":162},"CompareAndSwapPointer",[141,258,171],{"class":151},[141,260,226],{"class":147},[141,262,263],{"class":151},"s.head, head, unsafe.",[141,265,266],{"class":162},"Pointer",[141,268,269],{"class":151},"(node)) {\n",[141,271,273,276],{"class":143,"line":272},7,[141,274,275],{"class":147},"            return",[141,277,278],{"class":244},"  \u002F\u002F голова обновлена на новый элемент\n",[141,280,282],{"class":143,"line":281},8,[141,283,284],{"class":151},"        }\n",[141,286,288],{"class":143,"line":287},9,[141,289,290],{"class":244},"        \u002F\u002F кто-то встроился → повторяем\n",[141,292,294],{"class":143,"line":293},10,[141,295,296],{"class":151},"    }\n",[141,298,300],{"class":143,"line":299},11,[141,301,302],{"class":151},"}\n",[32,304,306],{"id":305},"pop","Pop",[37,308,310],{"className":135,"code":309,"language":137,"meta":5,"style":5},"func (s *Stack) Pop() int {\n    for {\n        head := atomic.LoadPointer(&s.head)\n        if head == nil {\n            return -1  \u002F\u002F стек пуст\n        }\n        next := atomic.LoadPointer(&(*Item)(head).next)\n        if atomic.CompareAndSwapPointer(&s.head, head, next) {\n            return (*Item)(head).value  \u002F\u002F голова сдвинута на next\n        }\n        \u002F\u002F кто-то встроился → повторяем\n    }\n}\n",[44,311,312,336,342,358,374,387,391,415,430,446,450,454,459],{"__ignoreMap":5},[141,313,314,316,318,320,322,324,326,328,331,334],{"class":143,"line":144},[141,315,148],{"class":147},[141,317,152],{"class":151},[141,319,156],{"class":155},[141,321,159],{"class":147},[141,323,163],{"class":162},[141,325,166],{"class":151},[141,327,306],{"class":162},[141,329,330],{"class":151},"() ",[141,332,333],{"class":147},"int",[141,335,207],{"class":151},[141,337,338,340],{"class":143,"line":183},[141,339,204],{"class":147},[141,341,207],{"class":151},[141,343,344,346,348,350,352,354,356],{"class":143,"line":201},[141,345,213],{"class":151},[141,347,189],{"class":147},[141,349,218],{"class":151},[141,351,221],{"class":162},[141,353,171],{"class":151},[141,355,226],{"class":147},[141,357,229],{"class":151},[141,359,360,362,365,368,372],{"class":143,"line":210},[141,361,251],{"class":147},[141,363,364],{"class":151}," head ",[141,366,367],{"class":147},"==",[141,369,371],{"class":370},"sDLfK"," nil",[141,373,207],{"class":151},[141,375,376,378,381,384],{"class":143,"line":232},[141,377,275],{"class":147},[141,379,380],{"class":147}," -",[141,382,383],{"class":370},"1",[141,385,386],{"class":244},"  \u002F\u002F стек пуст\n",[141,388,389],{"class":143,"line":248},[141,390,284],{"class":151},[141,392,393,396,398,400,402,404,406,408,410,412],{"class":143,"line":272},[141,394,395],{"class":151},"        next ",[141,397,189],{"class":147},[141,399,218],{"class":151},[141,401,221],{"class":162},[141,403,171],{"class":151},[141,405,226],{"class":147},[141,407,171],{"class":151},[141,409,159],{"class":147},[141,411,195],{"class":162},[141,413,414],{"class":151},")(head).next)\n",[141,416,417,419,421,423,425,427],{"class":143,"line":281},[141,418,251],{"class":147},[141,420,218],{"class":151},[141,422,256],{"class":162},[141,424,171],{"class":151},[141,426,226],{"class":147},[141,428,429],{"class":151},"s.head, head, next) {\n",[141,431,432,434,436,438,440,443],{"class":143,"line":287},[141,433,275],{"class":147},[141,435,152],{"class":151},[141,437,159],{"class":147},[141,439,195],{"class":162},[141,441,442],{"class":151},")(head).value  ",[141,444,445],{"class":244},"\u002F\u002F голова сдвинута на next\n",[141,447,448],{"class":143,"line":293},[141,449,284],{"class":151},[141,451,452],{"class":143,"line":299},[141,453,290],{"class":244},[141,455,457],{"class":143,"line":456},12,[141,458,296],{"class":151},[141,460,462],{"class":143,"line":461},13,[141,463,302],{"class":151},[32,465,467],{"id":466},"почему-cas-а-не-просто-присваивание","Почему CAS, а не просто присваивание",[47,469,470,471,474],{},"Между ",[44,472,473],{},"Load"," и обновлением головы другая горутина может изменить стек. CAS проверяет: \"голова всё ещё та, что я загрузил?\" Если да — обновляем. Если нет — повторяем.",[32,476,478],{"id":477},"проблема-высокий-contention","Проблема: высокий contention",[47,480,481,482,485],{},"У стека ",[51,483,484],{},"одна точка входа и выхода"," — голова. Все горутины конкурируют за неё. Много CAS-неудач → много повторных итераций.",[47,487,488],{},"Очередь лучше: голова (pop) и хвост (push) разнесены → меньше contention.",[24,490],{},[11,492,493,496,503],{},[14,494,495],{},"Очередь Майкла-Скотта — lock-free очередь на связанном списке. Базовая структура для многих lock-free алгоритмов",[14,497,498,499,502],{},"Push = два CAS (tail.next + tail). Между ними другая горутина может ",[51,500,501],{},"помочь"," довернуть второй CAS",[14,504,505],{},"Меньше contention чем стек: push работает с хвостом, pop — с головой",[24,507],{},[27,509,511],{"id":510},"lock-free-queue","Lock-free queue",[32,513,515],{"id":514},"ключевая-идея-помоги-другой-горутине","Ключевая идея: помоги другой горутине",[47,517,518],{},"В стеке один CAS. В очереди — два (обновить tail.next и tail). Между ними может встроиться другая горутина.",[47,520,521,522,525],{},"Решение: если горутина видит что кто-то начал push (tail.next ≠ nil), но не довернул tail — она ",[51,523,524],{},"сама довернёт"," чужой tail, а потом продолжит свой push.",[32,527,529],{"id":528},"структура","Структура",[37,531,533],{"className":135,"code":532,"language":137,"meta":5,"style":5},"type Item struct {\n    value int\n    next  unsafe.Pointer  \u002F\u002F atomic\n}\n\ntype Queue struct {\n    head unsafe.Pointer   \u002F\u002F dummy → first real element\n    tail unsafe.Pointer   \u002F\u002F last element\n}\n\nfunc NewQueue() *Queue {\n    dummy := &Item{}\n    q := &Queue{}\n    q.head = unsafe.Pointer(dummy)\n    q.tail = unsafe.Pointer(dummy)\n    return q\n}\n",[44,534,535,548,556,572,576,582,593,607,621,625,629,645,659,672,688,702,711],{"__ignoreMap":5},[141,536,537,540,543,546],{"class":143,"line":144},[141,538,539],{"class":147},"type",[141,541,542],{"class":162}," Item",[141,544,545],{"class":147}," struct",[141,547,207],{"class":151},[141,549,550,553],{"class":143,"line":183},[141,551,552],{"class":151},"    value ",[141,554,555],{"class":147},"int\n",[141,557,558,561,564,567,569],{"class":143,"line":201},[141,559,560],{"class":151},"    next  ",[141,562,563],{"class":162},"unsafe",[141,565,566],{"class":151},".",[141,568,266],{"class":162},[141,570,571],{"class":244},"  \u002F\u002F atomic\n",[141,573,574],{"class":143,"line":210},[141,575,302],{"class":151},[141,577,578],{"class":143,"line":232},[141,579,581],{"emptyLinePlaceholder":580},true,"\n",[141,583,584,586,589,591],{"class":143,"line":248},[141,585,539],{"class":147},[141,587,588],{"class":162}," Queue",[141,590,545],{"class":147},[141,592,207],{"class":151},[141,594,595,598,600,602,604],{"class":143,"line":272},[141,596,597],{"class":151},"    head ",[141,599,563],{"class":162},[141,601,566],{"class":151},[141,603,266],{"class":162},[141,605,606],{"class":244},"   \u002F\u002F dummy → first real element\n",[141,608,609,612,614,616,618],{"class":143,"line":281},[141,610,611],{"class":151},"    tail ",[141,613,563],{"class":162},[141,615,566],{"class":151},[141,617,266],{"class":162},[141,619,620],{"class":244},"   \u002F\u002F last element\n",[141,622,623],{"class":143,"line":287},[141,624,302],{"class":151},[141,626,627],{"class":143,"line":293},[141,628,581],{"emptyLinePlaceholder":580},[141,630,631,633,636,638,640,643],{"class":143,"line":299},[141,632,148],{"class":147},[141,634,635],{"class":162}," NewQueue",[141,637,330],{"class":151},[141,639,159],{"class":147},[141,641,642],{"class":162},"Queue",[141,644,207],{"class":151},[141,646,647,650,652,654,656],{"class":143,"line":456},[141,648,649],{"class":151},"    dummy ",[141,651,189],{"class":147},[141,653,192],{"class":147},[141,655,195],{"class":162},[141,657,658],{"class":151},"{}\n",[141,660,661,664,666,668,670],{"class":143,"line":461},[141,662,663],{"class":151},"    q ",[141,665,189],{"class":147},[141,667,192],{"class":147},[141,669,642],{"class":162},[141,671,658],{"class":151},[141,673,675,678,680,683,685],{"class":143,"line":674},14,[141,676,677],{"class":151},"    q.head ",[141,679,238],{"class":147},[141,681,682],{"class":151}," unsafe.",[141,684,266],{"class":162},[141,686,687],{"class":151},"(dummy)\n",[141,689,691,694,696,698,700],{"class":143,"line":690},15,[141,692,693],{"class":151},"    q.tail ",[141,695,238],{"class":147},[141,697,682],{"class":151},[141,699,266],{"class":162},[141,701,687],{"class":151},[141,703,705,708],{"class":143,"line":704},16,[141,706,707],{"class":147},"    return",[141,709,710],{"class":151}," q\n",[141,712,714],{"class":143,"line":713},17,[141,715,302],{"class":151},[47,717,718],{},"Dummy-элемент (пустышка) — чтобы не писать лишних if'ов. Head и tail начинают с dummy.",[32,720,132],{"id":721},"push-1",[37,723,725],{"className":135,"code":724,"language":137,"meta":5,"style":5},"func (q *Queue) Push(val int) {\n    node := &Item{value: val}\n    for {\n        tail := atomic.LoadPointer(&q.tail)\n        next := atomic.LoadPointer(&(*Item)(tail).next)\n\n        if tail != atomic.LoadPointer(&q.tail) {\n            continue  \u002F\u002F tail изменился, начинаем заново\n        }\n\n        if next == nil {\n            \u002F\u002F Никто не добавляет → CAS 1: tail.next = node\n            if atomic.CompareAndSwapPointer(&(*Item)(tail).next, nil, unsafe.Pointer(node)) {\n                \u002F\u002F CAS 2: tail = node (не проверяем успех!)\n                atomic.CompareAndSwapPointer(&q.tail, tail, unsafe.Pointer(node))\n                return\n            }\n        } else {\n            \u002F\u002F Кто-то начал push, но не довернул tail → ПОМОГАЕМ\n            atomic.CompareAndSwapPointer(&q.tail, tail, next)\n        }\n    }\n}\n",[44,726,727,752,764,770,788,811,815,836,844,848,852,865,870,902,907,926,931,936,947,953,968,973,978],{"__ignoreMap":5},[141,728,729,731,733,736,738,740,742,744,746,748,750],{"class":143,"line":144},[141,730,148],{"class":147},[141,732,152],{"class":151},[141,734,735],{"class":155},"q ",[141,737,159],{"class":147},[141,739,642],{"class":162},[141,741,166],{"class":151},[141,743,132],{"class":162},[141,745,171],{"class":151},[141,747,174],{"class":155},[141,749,177],{"class":147},[141,751,180],{"class":151},[141,753,754,756,758,760,762],{"class":143,"line":183},[141,755,186],{"class":151},[141,757,189],{"class":147},[141,759,192],{"class":147},[141,761,195],{"class":162},[141,763,198],{"class":151},[141,765,766,768],{"class":143,"line":201},[141,767,204],{"class":147},[141,769,207],{"class":151},[141,771,772,775,777,779,781,783,785],{"class":143,"line":210},[141,773,774],{"class":151},"        tail ",[141,776,189],{"class":147},[141,778,218],{"class":151},[141,780,221],{"class":162},[141,782,171],{"class":151},[141,784,226],{"class":147},[141,786,787],{"class":151},"q.tail)\n",[141,789,790,792,794,796,798,800,802,804,806,808],{"class":143,"line":232},[141,791,395],{"class":151},[141,793,189],{"class":147},[141,795,218],{"class":151},[141,797,221],{"class":162},[141,799,171],{"class":151},[141,801,226],{"class":147},[141,803,171],{"class":151},[141,805,159],{"class":147},[141,807,195],{"class":162},[141,809,810],{"class":151},")(tail).next)\n",[141,812,813],{"class":143,"line":248},[141,814,581],{"emptyLinePlaceholder":580},[141,816,817,819,822,825,827,829,831,833],{"class":143,"line":272},[141,818,251],{"class":147},[141,820,821],{"class":151}," tail ",[141,823,824],{"class":147},"!=",[141,826,218],{"class":151},[141,828,221],{"class":162},[141,830,171],{"class":151},[141,832,226],{"class":147},[141,834,835],{"class":151},"q.tail) {\n",[141,837,838,841],{"class":143,"line":281},[141,839,840],{"class":147},"            continue",[141,842,843],{"class":244},"  \u002F\u002F tail изменился, начинаем заново\n",[141,845,846],{"class":143,"line":287},[141,847,284],{"class":151},[141,849,850],{"class":143,"line":293},[141,851,581],{"emptyLinePlaceholder":580},[141,853,854,856,859,861,863],{"class":143,"line":299},[141,855,251],{"class":147},[141,857,858],{"class":151}," next ",[141,860,367],{"class":147},[141,862,371],{"class":370},[141,864,207],{"class":151},[141,866,867],{"class":143,"line":456},[141,868,869],{"class":244},"            \u002F\u002F Никто не добавляет → CAS 1: tail.next = node\n",[141,871,872,875,877,879,881,883,885,887,889,892,895,898,900],{"class":143,"line":461},[141,873,874],{"class":147},"            if",[141,876,218],{"class":151},[141,878,256],{"class":162},[141,880,171],{"class":151},[141,882,226],{"class":147},[141,884,171],{"class":151},[141,886,159],{"class":147},[141,888,195],{"class":162},[141,890,891],{"class":151},")(tail).next, ",[141,893,894],{"class":370},"nil",[141,896,897],{"class":151},", unsafe.",[141,899,266],{"class":162},[141,901,269],{"class":151},[141,903,904],{"class":143,"line":674},[141,905,906],{"class":244},"                \u002F\u002F CAS 2: tail = node (не проверяем успех!)\n",[141,908,909,912,914,916,918,921,923],{"class":143,"line":690},[141,910,911],{"class":151},"                atomic.",[141,913,256],{"class":162},[141,915,171],{"class":151},[141,917,226],{"class":147},[141,919,920],{"class":151},"q.tail, tail, unsafe.",[141,922,266],{"class":162},[141,924,925],{"class":151},"(node))\n",[141,927,928],{"class":143,"line":704},[141,929,930],{"class":147},"                return\n",[141,932,933],{"class":143,"line":713},[141,934,935],{"class":151},"            }\n",[141,937,939,942,945],{"class":143,"line":938},18,[141,940,941],{"class":151},"        } ",[141,943,944],{"class":147},"else",[141,946,207],{"class":151},[141,948,950],{"class":143,"line":949},19,[141,951,952],{"class":244},"            \u002F\u002F Кто-то начал push, но не довернул tail → ПОМОГАЕМ\n",[141,954,956,959,961,963,965],{"class":143,"line":955},20,[141,957,958],{"class":151},"            atomic.",[141,960,256],{"class":162},[141,962,171],{"class":151},[141,964,226],{"class":147},[141,966,967],{"class":151},"q.tail, tail, next)\n",[141,969,971],{"class":143,"line":970},21,[141,972,284],{"class":151},[141,974,976],{"class":143,"line":975},22,[141,977,296],{"class":151},[141,979,981],{"class":143,"line":980},23,[141,982,302],{"class":151},[47,984,985,988],{},[51,986,987],{},"CAS 2 не проверяется",": если не получилось — другая горутина поможет (ветка else).",[32,990,306],{"id":991},"pop-1",[37,993,995],{"className":135,"code":994,"language":137,"meta":5,"style":5},"func (q *Queue) Pop() int {\n    for {\n        head := atomic.LoadPointer(&q.head)\n        tail := atomic.LoadPointer(&q.tail)\n        next := atomic.LoadPointer(&(*Item)(head).next)\n\n        if head != atomic.LoadPointer(&q.head) { continue }\n\n        if head == tail {\n            if next == nil { return -1 }       \u002F\u002F пусто\n            \u002F\u002F Один элемент + кто-то пушит → помогаем довернуть tail\n            atomic.CompareAndSwapPointer(&q.tail, tail, next)\n        } else {\n            val := (*Item)(next).value\n            if atomic.CompareAndSwapPointer(&q.head, head, next) {\n                return val\n            }\n        }\n    }\n}\n",[44,996,997,1019,1025,1042,1058,1080,1084,1109,1113,1124,1150,1155,1167,1175,1191,1206,1214,1218,1222,1226],{"__ignoreMap":5},[141,998,999,1001,1003,1005,1007,1009,1011,1013,1015,1017],{"class":143,"line":144},[141,1000,148],{"class":147},[141,1002,152],{"class":151},[141,1004,735],{"class":155},[141,1006,159],{"class":147},[141,1008,642],{"class":162},[141,1010,166],{"class":151},[141,1012,306],{"class":162},[141,1014,330],{"class":151},[141,1016,333],{"class":147},[141,1018,207],{"class":151},[141,1020,1021,1023],{"class":143,"line":183},[141,1022,204],{"class":147},[141,1024,207],{"class":151},[141,1026,1027,1029,1031,1033,1035,1037,1039],{"class":143,"line":201},[141,1028,213],{"class":151},[141,1030,189],{"class":147},[141,1032,218],{"class":151},[141,1034,221],{"class":162},[141,1036,171],{"class":151},[141,1038,226],{"class":147},[141,1040,1041],{"class":151},"q.head)\n",[141,1043,1044,1046,1048,1050,1052,1054,1056],{"class":143,"line":210},[141,1045,774],{"class":151},[141,1047,189],{"class":147},[141,1049,218],{"class":151},[141,1051,221],{"class":162},[141,1053,171],{"class":151},[141,1055,226],{"class":147},[141,1057,787],{"class":151},[141,1059,1060,1062,1064,1066,1068,1070,1072,1074,1076,1078],{"class":143,"line":232},[141,1061,395],{"class":151},[141,1063,189],{"class":147},[141,1065,218],{"class":151},[141,1067,221],{"class":162},[141,1069,171],{"class":151},[141,1071,226],{"class":147},[141,1073,171],{"class":151},[141,1075,159],{"class":147},[141,1077,195],{"class":162},[141,1079,414],{"class":151},[141,1081,1082],{"class":143,"line":248},[141,1083,581],{"emptyLinePlaceholder":580},[141,1085,1086,1088,1090,1092,1094,1096,1098,1100,1103,1106],{"class":143,"line":272},[141,1087,251],{"class":147},[141,1089,364],{"class":151},[141,1091,824],{"class":147},[141,1093,218],{"class":151},[141,1095,221],{"class":162},[141,1097,171],{"class":151},[141,1099,226],{"class":147},[141,1101,1102],{"class":151},"q.head) { ",[141,1104,1105],{"class":147},"continue",[141,1107,1108],{"class":151}," }\n",[141,1110,1111],{"class":143,"line":281},[141,1112,581],{"emptyLinePlaceholder":580},[141,1114,1115,1117,1119,1121],{"class":143,"line":287},[141,1116,251],{"class":147},[141,1118,364],{"class":151},[141,1120,367],{"class":147},[141,1122,1123],{"class":151}," tail {\n",[141,1125,1126,1128,1130,1132,1134,1137,1140,1142,1144,1147],{"class":143,"line":293},[141,1127,874],{"class":147},[141,1129,858],{"class":151},[141,1131,367],{"class":147},[141,1133,371],{"class":370},[141,1135,1136],{"class":151}," { ",[141,1138,1139],{"class":147},"return",[141,1141,380],{"class":147},[141,1143,383],{"class":370},[141,1145,1146],{"class":151}," }       ",[141,1148,1149],{"class":244},"\u002F\u002F пусто\n",[141,1151,1152],{"class":143,"line":299},[141,1153,1154],{"class":244},"            \u002F\u002F Один элемент + кто-то пушит → помогаем довернуть tail\n",[141,1156,1157,1159,1161,1163,1165],{"class":143,"line":456},[141,1158,958],{"class":151},[141,1160,256],{"class":162},[141,1162,171],{"class":151},[141,1164,226],{"class":147},[141,1166,967],{"class":151},[141,1168,1169,1171,1173],{"class":143,"line":461},[141,1170,941],{"class":151},[141,1172,944],{"class":147},[141,1174,207],{"class":151},[141,1176,1177,1180,1182,1184,1186,1188],{"class":143,"line":674},[141,1178,1179],{"class":151},"            val ",[141,1181,189],{"class":147},[141,1183,152],{"class":151},[141,1185,159],{"class":147},[141,1187,195],{"class":162},[141,1189,1190],{"class":151},")(next).value\n",[141,1192,1193,1195,1197,1199,1201,1203],{"class":143,"line":690},[141,1194,874],{"class":147},[141,1196,218],{"class":151},[141,1198,256],{"class":162},[141,1200,171],{"class":151},[141,1202,226],{"class":147},[141,1204,1205],{"class":151},"q.head, head, next) {\n",[141,1207,1208,1211],{"class":143,"line":704},[141,1209,1210],{"class":147},"                return",[141,1212,1213],{"class":151}," val\n",[141,1215,1216],{"class":143,"line":713},[141,1217,935],{"class":151},[141,1219,1220],{"class":143,"line":938},[141,1221,284],{"class":151},[141,1223,1224],{"class":143,"line":949},[141,1225,296],{"class":151},[141,1227,1228],{"class":143,"line":955},[141,1229,302],{"class":151},[47,1231,1232,1235],{},[51,1233,1234],{},"Pop тоже помогает push",": если head == tail но next ≠ nil — элемент добавлен, но tail не обновлён. Pop доворачивает tail за чужую горутину.",[32,1237,1239],{"id":1238},"два-cas-общий-паттерн","Два CAS — общий паттерн",[47,1241,1242],{},"Когда операция требует >1 CAS, используй подход:",[1244,1245,1246,1253],"ol",{},[14,1247,1248,1249,1252],{},"Каждый CAS после первого — ",[51,1250,1251],{},"не проверяй"," успех",[14,1254,1255,1256,1259],{},"Другие горутины должны ",[51,1257,1258],{},"помогать"," довернуть незавершённые CAS",[47,1261,1262],{},"Это основа для любых lock-free структур сложнее стека.",[24,1264],{},[11,1266,1267,1270,1273],{},[14,1268,1269],{},"Акторная модель — математическая модель параллельных вычислений (1973, Карл Хьюит). Акторы общаются сообщениями, не разделяют состояние",[14,1271,1272],{},"Актор: inbox (буферизированный канал), внутреннее состояние (изолировано), уникальный адрес. 3 действия: создать актор, отправить сообщение, изменить своё состояние",[14,1274,1275],{},"В Go: актор = горутина + канал. Круто для распределённых систем (YDB, Erlang), избыточно для продуктовой бизнес-логики",[24,1277],{},[27,1279,1281],{"id":1280},"actor-model","Actor model",[32,1283,1285],{"id":1284},"структура-актора","Структура актора",[37,1287,1289],{"className":135,"code":1288,"language":137,"meta":5,"style":5},"type Actor struct {\n    inbox    chan Letter           \u002F\u002F почтовый ящик (буферизированный)\n    executor Executor              \u002F\u002F поведение (интерфейс)\n}\n\ntype Letter struct {\n    To   string\n    From string\n    Body interface{}\n}\n\nfunc NewActor(exec Executor) *Actor {\n    a := &Actor{inbox: make(chan Letter, 10), executor: exec}\n    go func() {\n        for letter := range a.inbox {   \u002F\u002F одна горутина обрабатывает\n            a.executor.Execute(letter)   \u002F\u002F последовательно (FIFO)\n        }\n    }()\n    return a\n}\n",[44,1290,1291,1302,1316,1327,1331,1335,1345,1353,1360,1370,1374,1378,1402,1434,1445,1464,1478,1482,1487,1494],{"__ignoreMap":5},[141,1292,1293,1295,1298,1300],{"class":143,"line":144},[141,1294,539],{"class":147},[141,1296,1297],{"class":162}," Actor",[141,1299,545],{"class":147},[141,1301,207],{"class":151},[141,1303,1304,1307,1310,1313],{"class":143,"line":183},[141,1305,1306],{"class":151},"    inbox    ",[141,1308,1309],{"class":147},"chan",[141,1311,1312],{"class":162}," Letter",[141,1314,1315],{"class":244},"           \u002F\u002F почтовый ящик (буферизированный)\n",[141,1317,1318,1321,1324],{"class":143,"line":201},[141,1319,1320],{"class":151},"    executor ",[141,1322,1323],{"class":162},"Executor",[141,1325,1326],{"class":244},"              \u002F\u002F поведение (интерфейс)\n",[141,1328,1329],{"class":143,"line":210},[141,1330,302],{"class":151},[141,1332,1333],{"class":143,"line":232},[141,1334,581],{"emptyLinePlaceholder":580},[141,1336,1337,1339,1341,1343],{"class":143,"line":248},[141,1338,539],{"class":147},[141,1340,1312],{"class":162},[141,1342,545],{"class":147},[141,1344,207],{"class":151},[141,1346,1347,1350],{"class":143,"line":272},[141,1348,1349],{"class":151},"    To   ",[141,1351,1352],{"class":147},"string\n",[141,1354,1355,1358],{"class":143,"line":281},[141,1356,1357],{"class":151},"    From ",[141,1359,1352],{"class":147},[141,1361,1362,1365,1368],{"class":143,"line":287},[141,1363,1364],{"class":151},"    Body ",[141,1366,1367],{"class":147},"interface",[141,1369,658],{"class":151},[141,1371,1372],{"class":143,"line":293},[141,1373,302],{"class":151},[141,1375,1376],{"class":143,"line":299},[141,1377,581],{"emptyLinePlaceholder":580},[141,1379,1380,1382,1385,1387,1390,1393,1395,1397,1400],{"class":143,"line":456},[141,1381,148],{"class":147},[141,1383,1384],{"class":162}," NewActor",[141,1386,171],{"class":151},[141,1388,1389],{"class":155},"exec",[141,1391,1392],{"class":162}," Executor",[141,1394,166],{"class":151},[141,1396,159],{"class":147},[141,1398,1399],{"class":162},"Actor",[141,1401,207],{"class":151},[141,1403,1404,1407,1409,1411,1413,1416,1419,1421,1423,1425,1428,1431],{"class":143,"line":461},[141,1405,1406],{"class":151},"    a ",[141,1408,189],{"class":147},[141,1410,192],{"class":147},[141,1412,1399],{"class":162},[141,1414,1415],{"class":151},"{inbox: ",[141,1417,1418],{"class":162},"make",[141,1420,171],{"class":151},[141,1422,1309],{"class":147},[141,1424,1312],{"class":162},[141,1426,1427],{"class":151},", ",[141,1429,1430],{"class":370},"10",[141,1432,1433],{"class":151},"), executor: exec}\n",[141,1435,1436,1439,1442],{"class":143,"line":674},[141,1437,1438],{"class":147},"    go",[141,1440,1441],{"class":147}," func",[141,1443,1444],{"class":151},"() {\n",[141,1446,1447,1450,1453,1455,1458,1461],{"class":143,"line":690},[141,1448,1449],{"class":147},"        for",[141,1451,1452],{"class":151}," letter ",[141,1454,189],{"class":147},[141,1456,1457],{"class":147}," range",[141,1459,1460],{"class":151}," a.inbox {   ",[141,1462,1463],{"class":244},"\u002F\u002F одна горутина обрабатывает\n",[141,1465,1466,1469,1472,1475],{"class":143,"line":704},[141,1467,1468],{"class":151},"            a.executor.",[141,1470,1471],{"class":162},"Execute",[141,1473,1474],{"class":151},"(letter)   ",[141,1476,1477],{"class":244},"\u002F\u002F последовательно (FIFO)\n",[141,1479,1480],{"class":143,"line":713},[141,1481,284],{"class":151},[141,1483,1484],{"class":143,"line":938},[141,1485,1486],{"class":151},"    }()\n",[141,1488,1489,1491],{"class":143,"line":949},[141,1490,707],{"class":147},[141,1492,1493],{"class":151}," a\n",[141,1495,1496],{"class":143,"line":955},[141,1497,302],{"class":151},[32,1499,1501],{"id":1500},"свойства","Свойства",[47,1503,1504,1507],{},[51,1505,1506],{},"Изоляция",": актор не может изменить состояние другого актора. Только отправить сообщение.",[47,1509,1510,1513],{},[51,1511,1512],{},"Асинхронность",": отправка = запись в буферизированный канал. Обработка — по одному, FIFO.",[47,1515,1516,1519],{},[51,1517,1518],{},"Параллелизм",": нужно обработать N сообщений параллельно → создать N акторов одного типа.",[47,1521,1522,1525,1526,1529],{},[51,1523,1524],{},"Actor Manager",": синглтон, хранит мапу ",[44,1527,1528],{},"адрес → актор",". Маршрутизирует сообщения. Под мьютексом.",[32,1531,1533],{"id":1532},"где-использовать","Где использовать",[47,1535,1536,1539],{},[51,1537,1538],{},"Да",": распределённые системы. Актор сегодня локальный (канал), завтра — удалённый (gRPC). YDB (C++), мониторинг Яндекса (Java), Erlang\u002FOTP.",[47,1541,1542,1545],{},[51,1543,1544],{},"Нет",": обычная продуктовая бизнес-логика. Избыточная абстракция, усложняет код.",[32,1547,1549],{"id":1548},"минусы","Минусы",[11,1551,1552,1555,1558],{},[14,1553,1554],{},"Как получать ошибки? Обратный канал? Две очереди (запрос\u002Fответ)?",[14,1556,1557],{},"Отладка сложнее: асинхронные цепочки вместо синхронных вызовов",[14,1559,1560],{},"Overhead на сериализацию\u002Fдесериализацию сообщений",[24,1562],{},[11,1564,1565,1568,1571],{},[14,1566,1567],{},"Закон Амдала: ускорение программы ограничено последовательной частью. S(N) = 1 \u002F ((1-P) + P\u002FN)",[14,1569,1570],{},"P = доля параллелизуемого кода, N = количество ядер. Даже бесконечное N не поможет если (1-P) > 0",[14,1572,1573],{},"Практическое применение: перед параллелизацией — оцени P, посчитай ожидаемое ускорение, сравни с бенчмарком",[24,1575],{},[27,1577,1579],{"id":1578},"contention-и-масштабирование","Contention и масштабирование",[32,1581,1583],{"id":1582},"формула","Формула",[37,1585,1588],{"className":1586,"code":1587,"language":42},[40],"           1\nS(N) = -----------\n       (1-P) + P\u002FN\n",[44,1589,1587],{"__ignoreMap":5},[47,1591,1592,1595,1596,1599,1600,1603,1604,1607],{},[51,1593,1594],{},"P"," — доля кода, которую можно распараллелить (0..1)\n",[51,1597,1598],{},"N"," — количество ядер (фактор параллельности)\n",[51,1601,1602],{},"1-P"," — доля кода, которая остаётся последовательной\n",[51,1605,1606],{},"S(N)"," — итоговое ускорение",[32,1609,1611],{"id":1610},"пример","Пример",[47,1613,1614],{},"90% кода параллелится (P=0.9), 10 ядер (N=10):",[37,1616,1619],{"className":1617,"code":1618,"language":42},[40],"S(10) = 1 \u002F (0.1 + 0.9\u002F10) = 1 \u002F (0.1 + 0.09) = 1 \u002F 0.19 = 5.26x\n",[44,1620,1618],{"__ignoreMap":5},[47,1622,1623],{},"Ожидали 10x, получили 5.26x. 10% последовательного кода съели половину ускорения.",[32,1625,1627],{"id":1626},"предел-при-n-бесконечности","Предел при N -> бесконечности",[37,1629,1632],{"className":1630,"code":1631,"language":42},[40],"S(inf) = 1 \u002F (1-P)\n\nP=0.5 -> максимум 2x (сколько бы ядер ни было)\nP=0.9 -> максимум 10x\nP=0.99 -> максимум 100x\n",[44,1633,1631],{"__ignoreMap":5},[32,1635,1637],{"id":1636},"практика-бенчмарк-подтверждает","Практика: бенчмарк подтверждает",[37,1639,1641],{"className":135,"code":1640,"language":137,"meta":5,"style":5},"func calculate(parallelFactor int) {\n    \u002F\u002F 50% последовательный код\n    for i := 0; i \u003C 1_000_000; i++ { \u002F* work *\u002F }\n\n    \u002F\u002F 50% параллелизуемый код\n    var wg sync.WaitGroup\n    chunk := 1_000_000 \u002F parallelFactor\n    for i := 0; i \u003C parallelFactor; i++ {\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            for j := 0; j \u003C chunk; j++ { \u002F* work *\u002F }\n        }()\n    }\n    wg.Wait()\n}\n",[44,1642,1643,1659,1664,1698,1702,1707,1723,1738,1759,1774,1783,1797,1825,1830,1834,1844],{"__ignoreMap":5},[141,1644,1645,1647,1650,1652,1655,1657],{"class":143,"line":144},[141,1646,148],{"class":147},[141,1648,1649],{"class":162}," calculate",[141,1651,171],{"class":151},[141,1653,1654],{"class":155},"parallelFactor",[141,1656,177],{"class":147},[141,1658,180],{"class":151},[141,1660,1661],{"class":143,"line":183},[141,1662,1663],{"class":244},"    \u002F\u002F 50% последовательный код\n",[141,1665,1666,1668,1671,1673,1676,1679,1682,1685,1688,1691,1693,1696],{"class":143,"line":201},[141,1667,204],{"class":147},[141,1669,1670],{"class":151}," i ",[141,1672,189],{"class":147},[141,1674,1675],{"class":370}," 0",[141,1677,1678],{"class":151},"; i ",[141,1680,1681],{"class":147},"\u003C",[141,1683,1684],{"class":370}," 1_000_000",[141,1686,1687],{"class":151},"; i",[141,1689,1690],{"class":147},"++",[141,1692,1136],{"class":151},[141,1694,1695],{"class":244},"\u002F* work *\u002F",[141,1697,1108],{"class":151},[141,1699,1700],{"class":143,"line":210},[141,1701,581],{"emptyLinePlaceholder":580},[141,1703,1704],{"class":143,"line":232},[141,1705,1706],{"class":244},"    \u002F\u002F 50% параллелизуемый код\n",[141,1708,1709,1712,1715,1718,1720],{"class":143,"line":248},[141,1710,1711],{"class":147},"    var",[141,1713,1714],{"class":151}," wg ",[141,1716,1717],{"class":162},"sync",[141,1719,566],{"class":151},[141,1721,1722],{"class":162},"WaitGroup\n",[141,1724,1725,1728,1730,1732,1735],{"class":143,"line":272},[141,1726,1727],{"class":151},"    chunk ",[141,1729,189],{"class":147},[141,1731,1684],{"class":370},[141,1733,1734],{"class":147}," \u002F",[141,1736,1737],{"class":151}," parallelFactor\n",[141,1739,1740,1742,1744,1746,1748,1750,1752,1755,1757],{"class":143,"line":281},[141,1741,204],{"class":147},[141,1743,1670],{"class":151},[141,1745,189],{"class":147},[141,1747,1675],{"class":370},[141,1749,1678],{"class":151},[141,1751,1681],{"class":147},[141,1753,1754],{"class":151}," parallelFactor; i",[141,1756,1690],{"class":147},[141,1758,207],{"class":151},[141,1760,1761,1764,1767,1769,1771],{"class":143,"line":287},[141,1762,1763],{"class":151},"        wg.",[141,1765,1766],{"class":162},"Add",[141,1768,171],{"class":151},[141,1770,383],{"class":370},[141,1772,1773],{"class":151},")\n",[141,1775,1776,1779,1781],{"class":143,"line":293},[141,1777,1778],{"class":147},"        go",[141,1780,1441],{"class":147},[141,1782,1444],{"class":151},[141,1784,1785,1788,1791,1794],{"class":143,"line":299},[141,1786,1787],{"class":147},"            defer",[141,1789,1790],{"class":151}," wg.",[141,1792,1793],{"class":162},"Done",[141,1795,1796],{"class":151},"()\n",[141,1798,1799,1802,1805,1807,1809,1812,1814,1817,1819,1821,1823],{"class":143,"line":456},[141,1800,1801],{"class":147},"            for",[141,1803,1804],{"class":151}," j ",[141,1806,189],{"class":147},[141,1808,1675],{"class":370},[141,1810,1811],{"class":151},"; j ",[141,1813,1681],{"class":147},[141,1815,1816],{"class":151}," chunk; j",[141,1818,1690],{"class":147},[141,1820,1136],{"class":151},[141,1822,1695],{"class":244},[141,1824,1108],{"class":151},[141,1826,1827],{"class":143,"line":461},[141,1828,1829],{"class":151},"        }()\n",[141,1831,1832],{"class":143,"line":674},[141,1833,296],{"class":151},[141,1835,1836,1839,1842],{"class":143,"line":690},[141,1837,1838],{"class":151},"    wg.",[141,1840,1841],{"class":162},"Wait",[141,1843,1796],{"class":151},[141,1845,1846],{"class":143,"line":704},[141,1847,302],{"class":151},[47,1849,1850],{},"P=0.5:",[37,1852,1855],{"className":1853,"code":1854,"language":42},[40],"N=1:  базовое время T\nN=2:  S = 1\u002F(0.5 + 0.25) = 1.33x  -> T\u002F1.33 (бенчмарк подтверждает)\nN=4:  S = 1\u002F(0.5 + 0.125) = 1.6x  -> T\u002F1.6  (бенчмарк подтверждает)\n",[44,1856,1854],{"__ignoreMap":5},[32,1858,1860],{"id":1859},"когда-применять","Когда применять",[47,1862,1863,1865],{},[51,1864,1538],{},": небольшая функция, можно глазами оценить P. Перед решением о параллелизации — посчитай.",[47,1867,1868,1870],{},[51,1869,1544],{},": огромная кодовая база, трудно оценить P.",[47,1872,1873,1876],{},[51,1874,1875],{},"Вывод",": если последовательная часть большая — параллелизация не поможет. Сначала оптимизируй последовательную часть.",[24,1878],{},[11,1880,1881,1884,1887],{},[14,1882,1883],{},"Шардированная мапа — разрезать одну мапу с одним мьютексом на N мап с N мьютексами",[14,1885,1886],{},"Роутинг по шардам: hash(key) % N. Горутины расходятся по разным шардам → меньше contention",[14,1888,1889],{},"Тот же принцип что локальные очереди в планировщике Go",[24,1891],{},[27,1893,1895],{"id":1894},"шардирование","Шардирование",[32,1897,1899],{"id":1898},"проблема","Проблема",[37,1901,1903],{"className":135,"code":1902,"language":137,"meta":5,"style":5},"type Cache struct {\n    mu   sync.Mutex\n    data map[string]string\n}\n",[44,1904,1905,1916,1928,1947],{"__ignoreMap":5},[141,1906,1907,1909,1912,1914],{"class":143,"line":144},[141,1908,539],{"class":147},[141,1910,1911],{"class":162}," Cache",[141,1913,545],{"class":147},[141,1915,207],{"class":151},[141,1917,1918,1921,1923,1925],{"class":143,"line":183},[141,1919,1920],{"class":151},"    mu   ",[141,1922,1717],{"class":162},[141,1924,566],{"class":151},[141,1926,1927],{"class":162},"Mutex\n",[141,1929,1930,1933,1936,1939,1942,1945],{"class":143,"line":201},[141,1931,1932],{"class":151},"    data ",[141,1934,1935],{"class":147},"map",[141,1937,1938],{"class":151},"[",[141,1940,1941],{"class":147},"string",[141,1943,1944],{"class":151},"]",[141,1946,1352],{"class":147},[141,1948,1949],{"class":143,"line":210},[141,1950,302],{"class":151},[47,1952,1953],{},"Один мьютекс → все горутины конкурируют за него → bottleneck.",[32,1955,1957],{"id":1956},"решение-шардирование","Решение: шардирование",[37,1959,1961],{"className":135,"code":1960,"language":137,"meta":5,"style":5},"type Shard struct {\n    mu   sync.RWMutex\n    data map[string]string\n}\n\ntype ShardedMap struct {\n    shards []Shard\n}\n\nfunc NewShardedMap(n int) *ShardedMap {\n    sm := &ShardedMap{shards: make([]Shard, n)}\n    for i := range sm.shards {\n        sm.shards[i].data = make(map[string]string)\n    }\n    return sm\n}\n\nfunc (sm *ShardedMap) getShard(key string) *Shard {\n    h := fnv32(key)\n    return &sm.shards[h%uint32(len(sm.shards))]\n}\n\nfunc (sm *ShardedMap) Get(key string) (string, bool) {\n    shard := sm.getShard(key)  \u002F\u002F без синхронизации — слайс не меняется\n    shard.mu.RLock()\n    defer shard.mu.RUnlock()\n    v, ok := shard.data[key]\n    return v, ok\n}\n",[44,1962,1963,1974,1985,1999,2003,2007,2018,2026,2030,2034,2057,2082,2095,2119,2123,2130,2134,2138,2172,2185,2205,2209,2213,2248,2267,2278,2292,2303,2311],{"__ignoreMap":5},[141,1964,1965,1967,1970,1972],{"class":143,"line":144},[141,1966,539],{"class":147},[141,1968,1969],{"class":162}," Shard",[141,1971,545],{"class":147},[141,1973,207],{"class":151},[141,1975,1976,1978,1980,1982],{"class":143,"line":183},[141,1977,1920],{"class":151},[141,1979,1717],{"class":162},[141,1981,566],{"class":151},[141,1983,1984],{"class":162},"RWMutex\n",[141,1986,1987,1989,1991,1993,1995,1997],{"class":143,"line":201},[141,1988,1932],{"class":151},[141,1990,1935],{"class":147},[141,1992,1938],{"class":151},[141,1994,1941],{"class":147},[141,1996,1944],{"class":151},[141,1998,1352],{"class":147},[141,2000,2001],{"class":143,"line":210},[141,2002,302],{"class":151},[141,2004,2005],{"class":143,"line":232},[141,2006,581],{"emptyLinePlaceholder":580},[141,2008,2009,2011,2014,2016],{"class":143,"line":248},[141,2010,539],{"class":147},[141,2012,2013],{"class":162}," ShardedMap",[141,2015,545],{"class":147},[141,2017,207],{"class":151},[141,2019,2020,2023],{"class":143,"line":272},[141,2021,2022],{"class":151},"    shards []",[141,2024,2025],{"class":162},"Shard\n",[141,2027,2028],{"class":143,"line":281},[141,2029,302],{"class":151},[141,2031,2032],{"class":143,"line":287},[141,2033,581],{"emptyLinePlaceholder":580},[141,2035,2036,2038,2041,2043,2046,2048,2050,2052,2055],{"class":143,"line":293},[141,2037,148],{"class":147},[141,2039,2040],{"class":162}," NewShardedMap",[141,2042,171],{"class":151},[141,2044,2045],{"class":155},"n",[141,2047,177],{"class":147},[141,2049,166],{"class":151},[141,2051,159],{"class":147},[141,2053,2054],{"class":162},"ShardedMap",[141,2056,207],{"class":151},[141,2058,2059,2062,2064,2066,2068,2071,2073,2076,2079],{"class":143,"line":299},[141,2060,2061],{"class":151},"    sm ",[141,2063,189],{"class":147},[141,2065,192],{"class":147},[141,2067,2054],{"class":162},[141,2069,2070],{"class":151},"{shards: ",[141,2072,1418],{"class":162},[141,2074,2075],{"class":151},"([]",[141,2077,2078],{"class":162},"Shard",[141,2080,2081],{"class":151},", n)}\n",[141,2083,2084,2086,2088,2090,2092],{"class":143,"line":456},[141,2085,204],{"class":147},[141,2087,1670],{"class":151},[141,2089,189],{"class":147},[141,2091,1457],{"class":147},[141,2093,2094],{"class":151}," sm.shards {\n",[141,2096,2097,2100,2102,2105,2107,2109,2111,2113,2115,2117],{"class":143,"line":461},[141,2098,2099],{"class":151},"        sm.shards[i].data ",[141,2101,238],{"class":147},[141,2103,2104],{"class":162}," make",[141,2106,171],{"class":151},[141,2108,1935],{"class":147},[141,2110,1938],{"class":151},[141,2112,1941],{"class":147},[141,2114,1944],{"class":151},[141,2116,1941],{"class":147},[141,2118,1773],{"class":151},[141,2120,2121],{"class":143,"line":674},[141,2122,296],{"class":151},[141,2124,2125,2127],{"class":143,"line":690},[141,2126,707],{"class":147},[141,2128,2129],{"class":151}," sm\n",[141,2131,2132],{"class":143,"line":704},[141,2133,302],{"class":151},[141,2135,2136],{"class":143,"line":713},[141,2137,581],{"emptyLinePlaceholder":580},[141,2139,2140,2142,2144,2147,2149,2151,2153,2156,2158,2161,2164,2166,2168,2170],{"class":143,"line":938},[141,2141,148],{"class":147},[141,2143,152],{"class":151},[141,2145,2146],{"class":155},"sm ",[141,2148,159],{"class":147},[141,2150,2054],{"class":162},[141,2152,166],{"class":151},[141,2154,2155],{"class":162},"getShard",[141,2157,171],{"class":151},[141,2159,2160],{"class":155},"key",[141,2162,2163],{"class":147}," string",[141,2165,166],{"class":151},[141,2167,159],{"class":147},[141,2169,2078],{"class":162},[141,2171,207],{"class":151},[141,2173,2174,2177,2179,2182],{"class":143,"line":949},[141,2175,2176],{"class":151},"    h ",[141,2178,189],{"class":147},[141,2180,2181],{"class":162}," fnv32",[141,2183,2184],{"class":151},"(key)\n",[141,2186,2187,2189,2191,2194,2197,2199,2202],{"class":143,"line":955},[141,2188,707],{"class":147},[141,2190,192],{"class":147},[141,2192,2193],{"class":151},"sm.shards[h",[141,2195,2196],{"class":147},"%uint32",[141,2198,171],{"class":151},[141,2200,2201],{"class":162},"len",[141,2203,2204],{"class":151},"(sm.shards))]\n",[141,2206,2207],{"class":143,"line":970},[141,2208,302],{"class":151},[141,2210,2211],{"class":143,"line":975},[141,2212,581],{"emptyLinePlaceholder":580},[141,2214,2215,2217,2219,2221,2223,2225,2227,2230,2232,2234,2236,2239,2241,2243,2246],{"class":143,"line":980},[141,2216,148],{"class":147},[141,2218,152],{"class":151},[141,2220,2146],{"class":155},[141,2222,159],{"class":147},[141,2224,2054],{"class":162},[141,2226,166],{"class":151},[141,2228,2229],{"class":162},"Get",[141,2231,171],{"class":151},[141,2233,2160],{"class":155},[141,2235,2163],{"class":147},[141,2237,2238],{"class":151},") (",[141,2240,1941],{"class":147},[141,2242,1427],{"class":151},[141,2244,2245],{"class":147},"bool",[141,2247,180],{"class":151},[141,2249,2251,2254,2256,2259,2261,2264],{"class":143,"line":2250},24,[141,2252,2253],{"class":151},"    shard ",[141,2255,189],{"class":147},[141,2257,2258],{"class":151}," sm.",[141,2260,2155],{"class":162},[141,2262,2263],{"class":151},"(key)  ",[141,2265,2266],{"class":244},"\u002F\u002F без синхронизации — слайс не меняется\n",[141,2268,2270,2273,2276],{"class":143,"line":2269},25,[141,2271,2272],{"class":151},"    shard.mu.",[141,2274,2275],{"class":162},"RLock",[141,2277,1796],{"class":151},[141,2279,2281,2284,2287,2290],{"class":143,"line":2280},26,[141,2282,2283],{"class":147},"    defer",[141,2285,2286],{"class":151}," shard.mu.",[141,2288,2289],{"class":162},"RUnlock",[141,2291,1796],{"class":151},[141,2293,2295,2298,2300],{"class":143,"line":2294},27,[141,2296,2297],{"class":151},"    v, ok ",[141,2299,189],{"class":147},[141,2301,2302],{"class":151}," shard.data[key]\n",[141,2304,2306,2308],{"class":143,"line":2305},28,[141,2307,707],{"class":147},[141,2309,2310],{"class":151}," v, ok\n",[141,2312,2314],{"class":143,"line":2313},29,[141,2315,302],{"class":151},[32,2317,2319],{"id":2318},"почему-работает","Почему работает",[47,2321,2322],{},"При N шардах и равномерном хэше: вероятность что 2 горутины попадут в один шард ≈ 1\u002FN. Чем больше N — тем меньше contention.",[37,2324,2327],{"className":2325,"code":2326,"language":42},[40],"1 шард:   все горутины → 1 мьютекс    (максимальный contention)\n16 шардов: горутины → 16 мьютексов    (contention \u002F 16)\n",[44,2328,2326],{"__ignoreMap":5},[32,2330,2332],{"id":2331},"выбор-количества-шардов","Выбор количества шардов",[11,2334,2335,2338,2341,2344],{},[14,2336,2337],{},"Слишком мало → мало выигрыша",[14,2339,2340],{},"Слишком много → overhead на память (N мьютексов + N мап)",[14,2342,2343],{},"Типичное значение: количество ядер × 4..16",[14,2345,2346],{},"Бенчмаркай под свою нагрузку",[32,2348,2350],{"id":2349},"продвинутый-вариант-внешний-роутинг","Продвинутый вариант: внешний роутинг",[47,2352,2353],{},"Можно вынести выбор шарда в интерфейс — вызывающий код явно роутит горутины к нужным шардам, чтобы определённые горутины никогда не пересекались.",[32,2355,2357],{"id":2356},"где-используется","Где используется",[11,2359,2360,2363,2366,2369],{},[14,2361,2362],{},"sync.Map (внутри Go runtime)",[14,2364,2365],{},"Локальные очереди планировщика Go (P-локальные)",[14,2367,2368],{},"Concurrent hash maps в Java (ConcurrentHashMap)",[14,2370,2371],{},"Шардирование в базах данных — та же идея",[24,2373],{},[11,2375,2376,2379,2382],{},[14,2377,2378],{},"RCU (Read-Copy-Update) — атомарная подмена указателя на целую структуру данных. Читатели работают с копией, писатель создаёт новую и подменяет указатель",[14,2380,2381],{},"Работает когда операции — только чтение, а запись = полная замена (не модификация)",[14,2383,2384],{},"Нет мьютексов, нет блокировок: только atomic.Store\u002FLoad на указателе",[24,2386],{},[27,2388,2390],{"id":2389},"optimistic-concurrency","Optimistic concurrency",[32,2392,125],{"id":2393},"идея-1",[47,2395,2396],{},"Есть кэш (мапа), который обновляется раз в минуту из внешнего хранилища. Между обновлениями — только чтение.",[47,2398,2399],{},[51,2400,2401],{},"С мьютексом:",[37,2403,2405],{"className":135,"code":2404,"language":137,"meta":5,"style":5},"type Cache struct {\n    mu   sync.Mutex\n    data map[string]string\n}\n\nfunc (c *Cache) Get(key string) string {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    return c.data[key]\n}\n",[44,2406,2407,2417,2427,2441,2445,2449,2479,2489,2501,2508],{"__ignoreMap":5},[141,2408,2409,2411,2413,2415],{"class":143,"line":144},[141,2410,539],{"class":147},[141,2412,1911],{"class":162},[141,2414,545],{"class":147},[141,2416,207],{"class":151},[141,2418,2419,2421,2423,2425],{"class":143,"line":183},[141,2420,1920],{"class":151},[141,2422,1717],{"class":162},[141,2424,566],{"class":151},[141,2426,1927],{"class":162},[141,2428,2429,2431,2433,2435,2437,2439],{"class":143,"line":201},[141,2430,1932],{"class":151},[141,2432,1935],{"class":147},[141,2434,1938],{"class":151},[141,2436,1941],{"class":147},[141,2438,1944],{"class":151},[141,2440,1352],{"class":147},[141,2442,2443],{"class":143,"line":210},[141,2444,302],{"class":151},[141,2446,2447],{"class":143,"line":232},[141,2448,581],{"emptyLinePlaceholder":580},[141,2450,2451,2453,2455,2458,2460,2463,2465,2467,2469,2471,2473,2475,2477],{"class":143,"line":248},[141,2452,148],{"class":147},[141,2454,152],{"class":151},[141,2456,2457],{"class":155},"c ",[141,2459,159],{"class":147},[141,2461,2462],{"class":162},"Cache",[141,2464,166],{"class":151},[141,2466,2229],{"class":162},[141,2468,171],{"class":151},[141,2470,2160],{"class":155},[141,2472,2163],{"class":147},[141,2474,166],{"class":151},[141,2476,1941],{"class":147},[141,2478,207],{"class":151},[141,2480,2481,2484,2487],{"class":143,"line":272},[141,2482,2483],{"class":151},"    c.mu.",[141,2485,2486],{"class":162},"Lock",[141,2488,1796],{"class":151},[141,2490,2491,2493,2496,2499],{"class":143,"line":281},[141,2492,2283],{"class":147},[141,2494,2495],{"class":151}," c.mu.",[141,2497,2498],{"class":162},"Unlock",[141,2500,1796],{"class":151},[141,2502,2503,2505],{"class":143,"line":287},[141,2504,707],{"class":147},[141,2506,2507],{"class":151}," c.data[key]\n",[141,2509,2510],{"class":143,"line":293},[141,2511,302],{"class":151},[47,2513,2514],{},[51,2515,2516],{},"С RCU (без мьютекса):",[37,2518,2520],{"className":135,"code":2519,"language":137,"meta":5,"style":5},"type Cache struct {\n    data unsafe.Pointer  \u002F\u002F *map[string]string\n}\n\nfunc (c *Cache) Get(key string) string {\n    m := (*map[string]string)(atomic.LoadPointer(&c.data))\n    return (*m)[key]\n}\n\nfunc (c *Cache) sync() {\n    newMap := loadFromStorage()           \u002F\u002F загрузка без блокировок\n    p := unsafe.Pointer(&newMap)\n    atomic.StorePointer(&c.data, p)       \u002F\u002F атомарная подмена\n}\n",[44,2521,2522,2532,2545,2549,2553,2581,2613,2624,2628,2632,2650,2666,2684,2702],{"__ignoreMap":5},[141,2523,2524,2526,2528,2530],{"class":143,"line":144},[141,2525,539],{"class":147},[141,2527,1911],{"class":162},[141,2529,545],{"class":147},[141,2531,207],{"class":151},[141,2533,2534,2536,2538,2540,2542],{"class":143,"line":183},[141,2535,1932],{"class":151},[141,2537,563],{"class":162},[141,2539,566],{"class":151},[141,2541,266],{"class":162},[141,2543,2544],{"class":244},"  \u002F\u002F *map[string]string\n",[141,2546,2547],{"class":143,"line":201},[141,2548,302],{"class":151},[141,2550,2551],{"class":143,"line":210},[141,2552,581],{"emptyLinePlaceholder":580},[141,2554,2555,2557,2559,2561,2563,2565,2567,2569,2571,2573,2575,2577,2579],{"class":143,"line":232},[141,2556,148],{"class":147},[141,2558,152],{"class":151},[141,2560,2457],{"class":155},[141,2562,159],{"class":147},[141,2564,2462],{"class":162},[141,2566,166],{"class":151},[141,2568,2229],{"class":162},[141,2570,171],{"class":151},[141,2572,2160],{"class":155},[141,2574,2163],{"class":147},[141,2576,166],{"class":151},[141,2578,1941],{"class":147},[141,2580,207],{"class":151},[141,2582,2583,2586,2588,2590,2593,2595,2597,2599,2601,2604,2606,2608,2610],{"class":143,"line":248},[141,2584,2585],{"class":151},"    m ",[141,2587,189],{"class":147},[141,2589,152],{"class":151},[141,2591,2592],{"class":147},"*map",[141,2594,1938],{"class":151},[141,2596,1941],{"class":147},[141,2598,1944],{"class":151},[141,2600,1941],{"class":147},[141,2602,2603],{"class":151},")(atomic.",[141,2605,221],{"class":162},[141,2607,171],{"class":151},[141,2609,226],{"class":147},[141,2611,2612],{"class":151},"c.data))\n",[141,2614,2615,2617,2619,2621],{"class":143,"line":272},[141,2616,707],{"class":147},[141,2618,152],{"class":151},[141,2620,159],{"class":147},[141,2622,2623],{"class":151},"m)[key]\n",[141,2625,2626],{"class":143,"line":281},[141,2627,302],{"class":151},[141,2629,2630],{"class":143,"line":287},[141,2631,581],{"emptyLinePlaceholder":580},[141,2633,2634,2636,2638,2640,2642,2644,2646,2648],{"class":143,"line":293},[141,2635,148],{"class":147},[141,2637,152],{"class":151},[141,2639,2457],{"class":155},[141,2641,159],{"class":147},[141,2643,2462],{"class":162},[141,2645,166],{"class":151},[141,2647,1717],{"class":162},[141,2649,1444],{"class":151},[141,2651,2652,2655,2657,2660,2663],{"class":143,"line":299},[141,2653,2654],{"class":151},"    newMap ",[141,2656,189],{"class":147},[141,2658,2659],{"class":162}," loadFromStorage",[141,2661,2662],{"class":151},"()           ",[141,2664,2665],{"class":244},"\u002F\u002F загрузка без блокировок\n",[141,2667,2668,2671,2673,2675,2677,2679,2681],{"class":143,"line":456},[141,2669,2670],{"class":151},"    p ",[141,2672,189],{"class":147},[141,2674,682],{"class":151},[141,2676,266],{"class":162},[141,2678,171],{"class":151},[141,2680,226],{"class":147},[141,2682,2683],{"class":151},"newMap)\n",[141,2685,2686,2689,2692,2694,2696,2699],{"class":143,"line":461},[141,2687,2688],{"class":151},"    atomic.",[141,2690,2691],{"class":162},"StorePointer",[141,2693,171],{"class":151},[141,2695,226],{"class":147},[141,2697,2698],{"class":151},"c.data, p)       ",[141,2700,2701],{"class":244},"\u002F\u002F атомарная подмена\n",[141,2703,2704],{"class":143,"line":674},[141,2705,302],{"class":151},[32,2707,2709],{"id":2708},"как-работает","Как работает",[37,2711,2714],{"className":2712,"code":2713,"language":42},[40],"Горутина A: Load → получила старую мапу → работает с ней\n                   ↓ (в это время)\nСинхронизатор: Store → подменил указатель на новую мапу\n                   ↓\nГорутина B: Load → получила НОВУЮ мапу → работает с ней\n",[44,2715,2713],{"__ignoreMap":5},[47,2717,2718,2719,2722],{},"A и B работают с ",[51,2720,2721],{},"разными"," копиями одновременно. Это нормально — данные обновляются раз в минуту, секундное расхождение некритично.",[47,2724,2725],{},"Когда A закончит — следующий Load вернёт уже новую мапу.",[32,2727,2729],{"id":2728},"когда-подходит","Когда подходит",[11,2731,2732,2735,2738],{},[14,2733,2734],{},"Кэш с периодическим обновлением (read-heavy, write-rare)",[14,2736,2737],{},"Конфигурация, загружаемая из внешнего источника",[14,2739,2740],{},"Любая структура где запись = полная замена, не модификация отдельных полей",[47,2742,2743,2746],{},[51,2744,2745],{},"Не подходит",": если нужно добавлять\u002Fудалять отдельные элементы конкурентно.",[24,2748],{},[11,2750,2751,2754,2757],{},[14,2752,2753],{},"4 уровня: грубая → тонкая → оптимистичная → ленивая синхронизация. Каждый увеличивает пропускную способность и сложность кода",[14,2755,2756],{},"Множество на отсортированном связанном списке как модельная структура. Метод find с двумя указателями (prev, curr)",[14,2758,2759],{},"Аналогия с БД: блокировка таблицы → блокировка строк → оптимистичные транзакции",[24,2761],{},[27,2763,2765],{"id":2764},"fine-grained-synchronization","Fine-grained synchronization",[32,2767,2769],{"id":2768},"модельная-задача","Модельная задача",[47,2771,2772],{},"Множество (set) на отсортированном связанном списке. Операции: contains, add, remove. Инвариант: элементы по возрастанию, без дубликатов.",[37,2774,2777],{"className":2775,"code":2776,"language":42},[40],"head(0) → 10 → 20 → 30 → 40 → nil\n",[44,2778,2776],{"__ignoreMap":5},[47,2780,2781],{},"find(val) бежит двумя указателями (prev, curr) пока curr.value \u003C val.",[32,2783,2785],{"id":2784},"_1-грубая-синхронизация-5972-строки","1. Грубая синхронизация (59→72 строки)",[37,2787,2789],{"className":135,"code":2788,"language":137,"meta":5,"style":5},"type Set struct {\n    mu   sync.Mutex\n    head *Node\n}\n",[44,2790,2791,2802,2812,2821],{"__ignoreMap":5},[141,2792,2793,2795,2798,2800],{"class":143,"line":144},[141,2794,539],{"class":147},[141,2796,2797],{"class":162}," Set",[141,2799,545],{"class":147},[141,2801,207],{"class":151},[141,2803,2804,2806,2808,2810],{"class":143,"line":183},[141,2805,1920],{"class":151},[141,2807,1717],{"class":162},[141,2809,566],{"class":151},[141,2811,1927],{"class":162},[141,2813,2814,2816,2818],{"class":143,"line":201},[141,2815,597],{"class":151},[141,2817,159],{"class":147},[141,2819,2820],{"class":162},"Node\n",[141,2822,2823],{"class":143,"line":210},[141,2824,302],{"class":151},[47,2826,2827],{},"Один мьютекс на всё. Lock перед каждой операцией. Просто, но все горутины конкурируют за один мьютекс.",[32,2829,2831],{"id":2830},"_2-тонкая-синхронизация-7285-строк","2. Тонкая синхронизация (72→85 строк)",[47,2833,2834,2835,2838],{},"Мьютекс в ",[51,2836,2837],{},"каждом узле",". Итерация = hand-over-hand locking:",[37,2840,2843],{"className":2841,"code":2842,"language":42},[40],"Lock(prev) → Lock(curr) → Unlock(prev) → prev=curr\n→ Lock(curr.next) → Unlock(prev) → ...\n",[44,2844,2842],{"__ignoreMap":5},[47,2846,2847,2848,2851],{},"Две горутины могут ",[51,2849,2850],{},"одновременно"," двигаться по списку если они на расстоянии друг от друга.",[47,2853,2854,2857],{},[51,2855,2856],{},"Удаление",": захватить prev + curr (2 мьютекса). Иначе аномалии: разрыв списка, потеря элементов.",[47,2859,2860,2863],{},[51,2861,2862],{},"Добавление",": формально хватает 1 мьютекса, но на практике захватывают 2 (переиспользование find).",[32,2865,2867],{"id":2866},"_3-оптимистичная-синхронизация-85145-строк","3. Оптимистичная синхронизация (85→145 строк)",[47,2869,2870,2871,2874],{},"Итерация ",[51,2872,2873],{},"без мьютексов"," (только атомики). Мьютексы захватываются только для модификации.",[47,2876,2877],{},"Но между \"нашёл элемент\" и \"захватил мьютекс\" кто-то мог удалить\u002Fдобавить.",[47,2879,2880,2883],{},[51,2881,2882],{},"Решение — валидация",": после захвата мьютексов пробежаться сначала списка → проверить что элементы ещё существуют и связь между ними цела.",[37,2885,2887],{"className":135,"code":2886,"language":137,"meta":5,"style":5},"func (s *Set) Add(val int) bool {\n    for {\n        prev, curr := s.find(val)         \u002F\u002F без мьютексов\n        prev.Lock(); curr.Lock()           \u002F\u002F захватили\n        if s.validate(prev, curr) {        \u002F\u002F повторный проход сначала\n            \u002F\u002F элементы на месте → добавляем\n            break\n        }\n        prev.Unlock(); curr.Unlock()       \u002F\u002F не валидно → заново\n    }\n}\n",[44,2888,2889,2918,2924,2943,2960,2975,2980,2985,2989,3005,3009],{"__ignoreMap":5},[141,2890,2891,2893,2895,2897,2899,2902,2904,2906,2908,2910,2912,2914,2916],{"class":143,"line":144},[141,2892,148],{"class":147},[141,2894,152],{"class":151},[141,2896,156],{"class":155},[141,2898,159],{"class":147},[141,2900,2901],{"class":162},"Set",[141,2903,166],{"class":151},[141,2905,1766],{"class":162},[141,2907,171],{"class":151},[141,2909,174],{"class":155},[141,2911,177],{"class":147},[141,2913,166],{"class":151},[141,2915,2245],{"class":147},[141,2917,207],{"class":151},[141,2919,2920,2922],{"class":143,"line":183},[141,2921,204],{"class":147},[141,2923,207],{"class":151},[141,2925,2926,2929,2931,2934,2937,2940],{"class":143,"line":201},[141,2927,2928],{"class":151},"        prev, curr ",[141,2930,189],{"class":147},[141,2932,2933],{"class":151}," s.",[141,2935,2936],{"class":162},"find",[141,2938,2939],{"class":151},"(val)         ",[141,2941,2942],{"class":244},"\u002F\u002F без мьютексов\n",[141,2944,2945,2948,2950,2953,2955,2957],{"class":143,"line":210},[141,2946,2947],{"class":151},"        prev.",[141,2949,2486],{"class":162},[141,2951,2952],{"class":151},"(); curr.",[141,2954,2486],{"class":162},[141,2956,2662],{"class":151},[141,2958,2959],{"class":244},"\u002F\u002F захватили\n",[141,2961,2962,2964,2966,2969,2972],{"class":143,"line":232},[141,2963,251],{"class":147},[141,2965,2933],{"class":151},[141,2967,2968],{"class":162},"validate",[141,2970,2971],{"class":151},"(prev, curr) {        ",[141,2973,2974],{"class":244},"\u002F\u002F повторный проход сначала\n",[141,2976,2977],{"class":143,"line":248},[141,2978,2979],{"class":244},"            \u002F\u002F элементы на месте → добавляем\n",[141,2981,2982],{"class":143,"line":272},[141,2983,2984],{"class":147},"            break\n",[141,2986,2987],{"class":143,"line":281},[141,2988,284],{"class":151},[141,2990,2991,2993,2995,2997,2999,3002],{"class":143,"line":287},[141,2992,2947],{"class":151},[141,2994,2498],{"class":162},[141,2996,2952],{"class":151},[141,2998,2498],{"class":162},[141,3000,3001],{"class":151},"()       ",[141,3003,3004],{"class":244},"\u002F\u002F не валидно → заново\n",[141,3006,3007],{"class":143,"line":293},[141,3008,296],{"class":151},[141,3010,3011],{"class":143,"line":299},[141,3012,302],{"class":151},[32,3014,3016],{"id":3015},"_4-ленивая-синхронизация-145130-строк","4. Ленивая синхронизация (145→~130 строк)",[47,3018,3019,3020,3023],{},"Вместо повторного прохода — ",[51,3021,3022],{},"флаг deleted"," в каждом узле.",[47,3025,3026,3027],{},"Валидация: ",[44,3028,3029],{},"!prev.dropped && !curr.dropped && prev.next == curr",[37,3031,3033],{"className":135,"code":3032,"language":137,"meta":5,"style":5},"func (s *Set) validate(prev, curr *Node) bool {\n    return !prev.dropped && !curr.dropped &&\n           prev.getNext() == curr\n}\n",[44,3034,3035,3073,3094,3109],{"__ignoreMap":5},[141,3036,3037,3039,3041,3043,3045,3047,3049,3051,3053,3056,3058,3061,3064,3067,3069,3071],{"class":143,"line":144},[141,3038,148],{"class":147},[141,3040,152],{"class":151},[141,3042,156],{"class":155},[141,3044,159],{"class":147},[141,3046,2901],{"class":162},[141,3048,166],{"class":151},[141,3050,2968],{"class":162},[141,3052,171],{"class":151},[141,3054,3055],{"class":155},"prev",[141,3057,1427],{"class":151},[141,3059,3060],{"class":155},"curr",[141,3062,3063],{"class":147}," *",[141,3065,3066],{"class":162},"Node",[141,3068,166],{"class":151},[141,3070,2245],{"class":147},[141,3072,207],{"class":151},[141,3074,3075,3077,3080,3083,3086,3088,3091],{"class":143,"line":183},[141,3076,707],{"class":147},[141,3078,3079],{"class":147}," !",[141,3081,3082],{"class":151},"prev.dropped ",[141,3084,3085],{"class":147},"&&",[141,3087,3079],{"class":147},[141,3089,3090],{"class":151},"curr.dropped ",[141,3092,3093],{"class":147},"&&\n",[141,3095,3096,3099,3102,3104,3106],{"class":143,"line":201},[141,3097,3098],{"class":151},"           prev.",[141,3100,3101],{"class":162},"getNext",[141,3103,330],{"class":151},[141,3105,367],{"class":147},[141,3107,3108],{"class":151}," curr\n",[141,3110,3111],{"class":143,"line":210},[141,3112,302],{"class":151},[47,3114,3115,3118],{},[51,3116,3117],{},"Важно",": внутри мьютексов всё равно нужны атомики для указателей — горутины бегают по списку без мьютексов (через атомики), поэтому модификация указателей должна быть атомарной.",[47,3120,3121,3122,3125,3126,566],{},"Удаление: сначала ",[44,3123,3124],{},"curr.dropped = true",", потом ",[44,3127,3128],{},"prev.next = curr.next",[32,3130,3132],{"id":3131},"сводка","Сводка",[37,3134,3137],{"className":3135,"code":3136,"language":42},[40],"                  Грубая    Тонкая    Оптимист.  Ленивая\nСтрок кода        72        85        145        ~130\nМьютексов          1         N (в узлах) N         N\nИтерация          под mx    hand-over  атомики    атомики\nВалидация         —         —          повт.проход  флаг\nContention        макс      меньше     ещё меньше  мин\n",[44,3138,3136],{"__ignoreMap":5},[32,3140,3142],{"id":3141},"дальнейшие-оптимизации","Дальнейшие оптимизации",[1244,3144,3145,3148,3151],{},[14,3146,3147],{},"Полностью lock-free (только атомики, без мьютексов)",[14,3149,3150],{},"Шардирование (разрезать список на N частей)",[14,3152,3153],{},"Комбинация: lock-free + шардирование",[24,3155],{},[11,3157,3158,3161,3164],{},[14,3159,3160],{},"Без правильной синхронизации: потеря элементов, разрыв списка, распад на два списка",[14,3162,3163],{},"Удаление: захвати prev + curr. Иначе параллельное удаление\u002Fдобавление соседних элементов ломает связи",[14,3165,3166],{},"Принцип: для любой модификации связи A→B нужна блокировка обоих концов",[24,3168],{},[27,3170,3172],{"id":3171},"аномалии-связного-списка","Аномалии связного списка",[32,3174,3176],{"id":3175},"аномалия-1-потеря-добавленного-элемента","Аномалия 1: потеря добавленного элемента",[37,3178,3181],{"className":3179,"code":3180,"language":42},[40],"Начало:    10 → 20 → 30 → 40\n\nГорутина 1: add(25) — нашла позицию между 20 и 30, засыпает\nГорутина 2: add(27) — добавила между 20 и 30\n            10 → 20 → 27 → 30 → 40\n\nГорутина 1: просыпается, ставит 25.next = 30 (старый next 20)\n            20.next = 25\n            Результат: 10 → 20 → 25 → 30 → 40\n            Элемент 27 потерян!\n",[44,3182,3180],{"__ignoreMap":5},[47,3184,3185],{},"Проблема: горутина 1 использует устаревшую связь 20→30.",[32,3187,3189],{"id":3188},"аномалия-2-потеря-элемента-при-удалении","Аномалия 2: потеря элемента при удалении",[37,3191,3194],{"className":3192,"code":3193,"language":42},[40],"Начало:    10 → 20 → 30 → 40\n\nГорутина 1: remove(20) — засыпает перед обновлением связи\nГорутина 2: remove(30) — удаляет: 20.next = 40\n            10 → 20 → 40\n\nГорутина 1: просыпается, удаляет 20: 10.next = 30\n            Но 30 уже удалён!\n            10 → 30 → ???  (список сломан)\n",[44,3195,3193],{"__ignoreMap":5},[32,3197,3199],{"id":3198},"аномалия-3-распад-списка","Аномалия 3: распад списка",[37,3201,3204],{"className":3202,"code":3203,"language":42},[40],"Начало:    10 → 20 → 30 → 40\n\nГорутина 1: remove(30) — засыпает\nГорутина 2: remove(20) — удаляет: 10.next = 30\n            10 → 30 → 40\n\nГорутина 1: просыпается, удаляет 30: 20.next = 40\n            Но 20 уже не в списке!\n            Список распался на два:  10 → 30  и  20 → 40\n",[44,3205,3203],{"__ignoreMap":5},[32,3207,3209],{"id":3208},"решение-захват-prev-curr","Решение: захват prev + curr",[47,3211,3212],{},"Для удаления элемента 30: Lock(20) + Lock(30).",[37,3214,3217],{"className":3215,"code":3216,"language":42},[40],"  Lock(20)  Lock(30)\n     ↓         ↓\n→ [20] ----→ [30] → 40\n",[44,3218,3216],{"__ignoreMap":5},[47,3220,3221],{},"Теперь никто не может:",[11,3223,3224,3227,3230,3233],{},[14,3225,3226],{},"Удалить 20 (нужен Lock(20) — занят)",[14,3228,3229],{},"Удалить 30 (нужен Lock(30) — занят)",[14,3231,3232],{},"Удалить 40 (нужен Lock(30) как prev — занят)",[14,3234,3235],{},"Добавить между 20 и 30 (нужен Lock(20) или Lock(30) — заняты)",[47,3237,3238,3239,3242],{},"Элемент 10 ",[51,3240,3241],{},"можно"," удалить — его блокировка свободна, и это не влияет на операцию с 30.",[32,3244,3246],{"id":3245},"deadlock-prevention","Deadlock prevention",[47,3248,3249],{},"Hand-over-hand: всегда захватываем в порядке prev → curr (слева направо). Все горутины двигаются в одном направлении → циклических зависимостей нет → deadlock невозможен.",[24,3251],{},[27,3253,3255],{"id":3254},"практика","Практика",[3257,3258,3261,3264,3281],"quiz",{"answer":3259,"id":3260,"xp":1430},"2","concurrency-lockfree-q1",[47,3262,3263],{},"Что означает операция compare-and-swap (CAS)?",[3265,3266,3267],"template",{"v-slot:options":5},[11,3268,3269,3272,3275,3278],{},[14,3270,3271],{},"Записать новое значение без проверки старого",[14,3273,3274],{},"Сравнить текущее значение с ожидаемым и заменить его только если оно не изменилось",[14,3276,3277],{},"Заблокировать mutex, сравнить два указателя и разблокировать mutex",[14,3279,3280],{},"Дождаться, пока все горутины завершат чтение значения",[3265,3282,3283],{"v-slot:explanation":5},[47,3284,3285,3286,3289],{},"CAS атомарно проверяет, что значение всё ещё равно ожидаемому, и только после этого выполняет замену. Если другая горутина успела изменить значение, CAS возвращает ",[44,3287,3288],{},"false",", а код обычно перечитывает состояние и пробует снова.",[3291,3292,3296,3299,3447],"predict",{"answer":3293,"id":3294,"xp":3295},"true\\nfalse\\n10","concurrency-lockfree-p1","15",[47,3297,3298],{},"Что выведет программа?",[3265,3300,3301],{"v-slot:code":5},[37,3302,3304],{"className":135,"code":3303,"language":137,"meta":5,"style":5},"package main\n\nimport (\n    \"fmt\"\n    \"sync\u002Fatomic\"\n)\n\nfunc main() {\n    var n atomic.Int64\n\n    fmt.Println(n.CompareAndSwap(0, 10))\n    fmt.Println(n.CompareAndSwap(0, 20))\n    fmt.Println(n.Load())\n}\n",[44,3305,3306,3314,3318,3326,3338,3347,3351,3355,3364,3379,3383,3409,3430,3443],{"__ignoreMap":5},[141,3307,3308,3311],{"class":143,"line":144},[141,3309,3310],{"class":147},"package",[141,3312,3313],{"class":162}," main\n",[141,3315,3316],{"class":143,"line":183},[141,3317,581],{"emptyLinePlaceholder":580},[141,3319,3320,3323],{"class":143,"line":201},[141,3321,3322],{"class":147},"import",[141,3324,3325],{"class":151}," (\n",[141,3327,3328,3332,3335],{"class":143,"line":210},[141,3329,3331],{"class":3330},"sU2Wk","    \"",[141,3333,3334],{"class":162},"fmt",[141,3336,3337],{"class":3330},"\"\n",[141,3339,3340,3342,3345],{"class":143,"line":232},[141,3341,3331],{"class":3330},[141,3343,3344],{"class":162},"sync\u002Fatomic",[141,3346,3337],{"class":3330},[141,3348,3349],{"class":143,"line":248},[141,3350,1773],{"class":151},[141,3352,3353],{"class":143,"line":272},[141,3354,581],{"emptyLinePlaceholder":580},[141,3356,3357,3359,3362],{"class":143,"line":281},[141,3358,148],{"class":147},[141,3360,3361],{"class":162}," main",[141,3363,1444],{"class":151},[141,3365,3366,3368,3371,3374,3376],{"class":143,"line":287},[141,3367,1711],{"class":147},[141,3369,3370],{"class":151}," n ",[141,3372,3373],{"class":162},"atomic",[141,3375,566],{"class":151},[141,3377,3378],{"class":162},"Int64\n",[141,3380,3381],{"class":143,"line":293},[141,3382,581],{"emptyLinePlaceholder":580},[141,3384,3385,3388,3391,3394,3397,3399,3402,3404,3406],{"class":143,"line":299},[141,3386,3387],{"class":151},"    fmt.",[141,3389,3390],{"class":162},"Println",[141,3392,3393],{"class":151},"(n.",[141,3395,3396],{"class":162},"CompareAndSwap",[141,3398,171],{"class":151},[141,3400,3401],{"class":370},"0",[141,3403,1427],{"class":151},[141,3405,1430],{"class":370},[141,3407,3408],{"class":151},"))\n",[141,3410,3411,3413,3415,3417,3419,3421,3423,3425,3428],{"class":143,"line":456},[141,3412,3387],{"class":151},[141,3414,3390],{"class":162},[141,3416,3393],{"class":151},[141,3418,3396],{"class":162},[141,3420,171],{"class":151},[141,3422,3401],{"class":370},[141,3424,1427],{"class":151},[141,3426,3427],{"class":370},"20",[141,3429,3408],{"class":151},[141,3431,3432,3434,3436,3438,3440],{"class":143,"line":461},[141,3433,3387],{"class":151},[141,3435,3390],{"class":162},[141,3437,3393],{"class":151},[141,3439,473],{"class":162},[141,3441,3442],{"class":151},"())\n",[141,3444,3445],{"class":143,"line":674},[141,3446,302],{"class":151},[3265,3448,3449],{"v-slot:hint":5},[47,3450,3451,3452,3454,3455,3457,3458,3460,3461,3463],{},"Первый CAS видит ожидаемый ",[44,3453,3401],{}," и меняет значение на ",[44,3456,1430],{},". Второй CAS всё ещё ожидает ",[44,3459,3401],{},", но текущее значение уже ",[44,3462,1430],{},", поэтому замена не происходит.",[3465,3466,3469,3484,3770],"code-task",{"expected":3467,"id":3468,"xp":3427},"5","concurrency-lockfree-ct1",[47,3470,3471,3472,3475,3476,3479,3480,3483],{},"Реализуй ",[44,3473,3474],{},"AtomicCounter"," без mutex: ",[44,3477,3478],{},"Inc"," должен атомарно увеличивать счётчик на 1, а ",[44,3481,3482],{},"Value"," — возвращать текущее значение.",[3265,3485,3486],{"v-slot:template":5},[37,3487,3489],{"className":135,"code":3488,"language":137,"meta":5,"style":5},"package main\n\nimport (\n    \"fmt\"\n    \"sync\"\n    \"sync\u002Fatomic\"\n)\n\ntype AtomicCounter struct {\n    value atomic.Int64\n}\n\nfunc (c *AtomicCounter) Inc() {\n}\n\nfunc (c *AtomicCounter) Value() int64 {\n    return 0\n}\n\nfunc main() {\n    var counter AtomicCounter\n    var wg sync.WaitGroup\n\n    for i := 0; i \u003C 5; 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",[44,3490,3491,3497,3501,3507,3515,3523,3531,3535,3539,3550,3560,3564,3568,3586,3590,3594,3617,3624,3628,3632,3640,3650,3662,3666,3689,3701,3709,3719,3728,3732,3737,3742,3751,3765],{"__ignoreMap":5},[141,3492,3493,3495],{"class":143,"line":144},[141,3494,3310],{"class":147},[141,3496,3313],{"class":162},[141,3498,3499],{"class":143,"line":183},[141,3500,581],{"emptyLinePlaceholder":580},[141,3502,3503,3505],{"class":143,"line":201},[141,3504,3322],{"class":147},[141,3506,3325],{"class":151},[141,3508,3509,3511,3513],{"class":143,"line":210},[141,3510,3331],{"class":3330},[141,3512,3334],{"class":162},[141,3514,3337],{"class":3330},[141,3516,3517,3519,3521],{"class":143,"line":232},[141,3518,3331],{"class":3330},[141,3520,1717],{"class":162},[141,3522,3337],{"class":3330},[141,3524,3525,3527,3529],{"class":143,"line":248},[141,3526,3331],{"class":3330},[141,3528,3344],{"class":162},[141,3530,3337],{"class":3330},[141,3532,3533],{"class":143,"line":272},[141,3534,1773],{"class":151},[141,3536,3537],{"class":143,"line":281},[141,3538,581],{"emptyLinePlaceholder":580},[141,3540,3541,3543,3546,3548],{"class":143,"line":287},[141,3542,539],{"class":147},[141,3544,3545],{"class":162}," AtomicCounter",[141,3547,545],{"class":147},[141,3549,207],{"class":151},[141,3551,3552,3554,3556,3558],{"class":143,"line":293},[141,3553,552],{"class":151},[141,3555,3373],{"class":162},[141,3557,566],{"class":151},[141,3559,3378],{"class":162},[141,3561,3562],{"class":143,"line":299},[141,3563,302],{"class":151},[141,3565,3566],{"class":143,"line":456},[141,3567,581],{"emptyLinePlaceholder":580},[141,3569,3570,3572,3574,3576,3578,3580,3582,3584],{"class":143,"line":461},[141,3571,148],{"class":147},[141,3573,152],{"class":151},[141,3575,2457],{"class":155},[141,3577,159],{"class":147},[141,3579,3474],{"class":162},[141,3581,166],{"class":151},[141,3583,3478],{"class":162},[141,3585,1444],{"class":151},[141,3587,3588],{"class":143,"line":674},[141,3589,302],{"class":151},[141,3591,3592],{"class":143,"line":690},[141,3593,581],{"emptyLinePlaceholder":580},[141,3595,3596,3598,3600,3602,3604,3606,3608,3610,3612,3615],{"class":143,"line":704},[141,3597,148],{"class":147},[141,3599,152],{"class":151},[141,3601,2457],{"class":155},[141,3603,159],{"class":147},[141,3605,3474],{"class":162},[141,3607,166],{"class":151},[141,3609,3482],{"class":162},[141,3611,330],{"class":151},[141,3613,3614],{"class":147},"int64",[141,3616,207],{"class":151},[141,3618,3619,3621],{"class":143,"line":713},[141,3620,707],{"class":147},[141,3622,3623],{"class":370}," 0\n",[141,3625,3626],{"class":143,"line":938},[141,3627,302],{"class":151},[141,3629,3630],{"class":143,"line":949},[141,3631,581],{"emptyLinePlaceholder":580},[141,3633,3634,3636,3638],{"class":143,"line":955},[141,3635,148],{"class":147},[141,3637,3361],{"class":162},[141,3639,1444],{"class":151},[141,3641,3642,3644,3647],{"class":143,"line":970},[141,3643,1711],{"class":147},[141,3645,3646],{"class":151}," counter ",[141,3648,3649],{"class":162},"AtomicCounter\n",[141,3651,3652,3654,3656,3658,3660],{"class":143,"line":975},[141,3653,1711],{"class":147},[141,3655,1714],{"class":151},[141,3657,1717],{"class":162},[141,3659,566],{"class":151},[141,3661,1722],{"class":162},[141,3663,3664],{"class":143,"line":980},[141,3665,581],{"emptyLinePlaceholder":580},[141,3667,3668,3670,3672,3674,3676,3678,3680,3683,3685,3687],{"class":143,"line":2250},[141,3669,204],{"class":147},[141,3671,1670],{"class":151},[141,3673,189],{"class":147},[141,3675,1675],{"class":370},[141,3677,1678],{"class":151},[141,3679,1681],{"class":147},[141,3681,3682],{"class":370}," 5",[141,3684,1687],{"class":151},[141,3686,1690],{"class":147},[141,3688,207],{"class":151},[141,3690,3691,3693,3695,3697,3699],{"class":143,"line":2269},[141,3692,1763],{"class":151},[141,3694,1766],{"class":162},[141,3696,171],{"class":151},[141,3698,383],{"class":370},[141,3700,1773],{"class":151},[141,3702,3703,3705,3707],{"class":143,"line":2280},[141,3704,1778],{"class":147},[141,3706,1441],{"class":147},[141,3708,1444],{"class":151},[141,3710,3711,3713,3715,3717],{"class":143,"line":2294},[141,3712,1787],{"class":147},[141,3714,1790],{"class":151},[141,3716,1793],{"class":162},[141,3718,1796],{"class":151},[141,3720,3721,3724,3726],{"class":143,"line":2305},[141,3722,3723],{"class":151},"            counter.",[141,3725,3478],{"class":162},[141,3727,1796],{"class":151},[141,3729,3730],{"class":143,"line":2313},[141,3731,1829],{"class":151},[141,3733,3735],{"class":143,"line":3734},30,[141,3736,296],{"class":151},[141,3738,3740],{"class":143,"line":3739},31,[141,3741,581],{"emptyLinePlaceholder":580},[141,3743,3745,3747,3749],{"class":143,"line":3744},32,[141,3746,1838],{"class":151},[141,3748,1841],{"class":162},[141,3750,1796],{"class":151},[141,3752,3754,3756,3758,3761,3763],{"class":143,"line":3753},33,[141,3755,3387],{"class":151},[141,3757,3390],{"class":162},[141,3759,3760],{"class":151},"(counter.",[141,3762,3482],{"class":162},[141,3764,3442],{"class":151},[141,3766,3768],{"class":143,"line":3767},34,[141,3769,302],{"class":151},[3265,3771,3772],{"v-slot:hints":5},[11,3773,3774,3784,3790],{},[14,3775,3776,3777,3780,3781],{},"У ",[44,3778,3779],{},"atomic.Int64"," есть метод ",[44,3782,3783],{},"Add(delta)",[14,3785,3786,3787],{},"Для чтения используй ",[44,3788,3789],{},"Load()",[14,3791,3792,3793,3796],{},"После ",[44,3794,3795],{},"wg.Wait()"," все пять инкрементов уже завершены",[3798,3799,3800],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}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 pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}",{"title":5,"searchDepth":183,"depth":183,"links":3802},[3803,3808,3815,3822,3828,3835,3843,3848,3857,3864],{"id":29,"depth":183,"text":30,"children":3804},[3805,3806,3807],{"id":34,"depth":201,"text":35},{"id":61,"depth":201,"text":62},{"id":74,"depth":201,"text":75},{"id":120,"depth":183,"text":121,"children":3809},[3810,3811,3812,3813,3814],{"id":124,"depth":201,"text":125},{"id":131,"depth":201,"text":132},{"id":305,"depth":201,"text":306},{"id":466,"depth":201,"text":467},{"id":477,"depth":201,"text":478},{"id":510,"depth":183,"text":511,"children":3816},[3817,3818,3819,3820,3821],{"id":514,"depth":201,"text":515},{"id":528,"depth":201,"text":529},{"id":721,"depth":201,"text":132},{"id":991,"depth":201,"text":306},{"id":1238,"depth":201,"text":1239},{"id":1280,"depth":183,"text":1281,"children":3823},[3824,3825,3826,3827],{"id":1284,"depth":201,"text":1285},{"id":1500,"depth":201,"text":1501},{"id":1532,"depth":201,"text":1533},{"id":1548,"depth":201,"text":1549},{"id":1578,"depth":183,"text":1579,"children":3829},[3830,3831,3832,3833,3834],{"id":1582,"depth":201,"text":1583},{"id":1610,"depth":201,"text":1611},{"id":1626,"depth":201,"text":1627},{"id":1636,"depth":201,"text":1637},{"id":1859,"depth":201,"text":1860},{"id":1894,"depth":183,"text":1895,"children":3836},[3837,3838,3839,3840,3841,3842],{"id":1898,"depth":201,"text":1899},{"id":1956,"depth":201,"text":1957},{"id":2318,"depth":201,"text":2319},{"id":2331,"depth":201,"text":2332},{"id":2349,"depth":201,"text":2350},{"id":2356,"depth":201,"text":2357},{"id":2389,"depth":183,"text":2390,"children":3844},[3845,3846,3847],{"id":2393,"depth":201,"text":125},{"id":2708,"depth":201,"text":2709},{"id":2728,"depth":201,"text":2729},{"id":2764,"depth":183,"text":2765,"children":3849},[3850,3851,3852,3853,3854,3855,3856],{"id":2768,"depth":201,"text":2769},{"id":2784,"depth":201,"text":2785},{"id":2830,"depth":201,"text":2831},{"id":2866,"depth":201,"text":2867},{"id":3015,"depth":201,"text":3016},{"id":3131,"depth":201,"text":3132},{"id":3141,"depth":201,"text":3142},{"id":3171,"depth":183,"text":3172,"children":3858},[3859,3860,3861,3862,3863],{"id":3175,"depth":201,"text":3176},{"id":3188,"depth":201,"text":3189},{"id":3198,"depth":201,"text":3199},{"id":3208,"depth":201,"text":3209},{"id":3245,"depth":201,"text":3246},{"id":3254,"depth":183,"text":3255},1781458320273]