[{"data":1,"prerenderedAt":6260},["ShallowReactive",2],{"wiki-page-\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1300-std-atomic":3,"wiki-doc-items-2023-10-05-cplusplus-jiao-xue":5700,"wiki:author-members":6248},{"id":4,"title":5,"authors":6,"body":8,"chapterDepth":125,"chapterOrder":5687,"date":5688,"description":5689,"docKey":5690,"docRoot":5691,"docTitle":5692,"extension":5693,"isWikiDoc":128,"isWikiIndex":5694,"meta":5695,"navigation":128,"path":5696,"seo":5697,"stem":5698,"wikiDepth":32,"__hash__":5699},"wiki\u002Fwiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1300-std-atomic.md","std::atomic",[7],"董佳辉",{"type":9,"value":10,"toc":5642},"minimark",[11,19,41,44,47,55,58,65,68,76,87,92,96,99,212,219,225,236,239,245,248,254,257,264,267,292,303,306,365,368,396,405,408,414,417,420,456,466,473,480,483,489,498,783,788,794,797,817,820,859,862,869,879,882,898,901,903,928,941,947,952,962,1004,1007,1013,1016,1019,1025,1028,1034,1039,1042,1073,1076,1083,1327,1331,1337,1340,1347,1353,1360,1363,1369,1375,1385,1395,1401,1404,1431,1441,1444,1450,1452,1483,1486,1492,1498,1501,1506,1509,1515,1518,1522,1529,1532,1535,1538,1543,1546,1552,1555,1574,1577,1705,1709,1715,1721,1776,1779,1785,1791,1794,1800,1803,1809,1818,1824,1827,1884,1890,1893,1908,1911,1917,1920,1926,1932,1940,1943,1949,1952,1971,1974,1988,1995,2012,2014,2031,2034,2040,2043,2063,2066,2072,2075,2093,2096,2102,2105,2170,2173,2179,2182,2194,2205,2229,2232,2266,2269,2282,2285,2291,2294,2299,2306,2309,2315,2318,2323,2330,2333,2336,2359,2362,2364,2439,2442,2445,2491,2494,2500,2503,2509,2512,2524,2527,2553,2556,2561,2572,2575,2581,2584,2589,2608,2611,2617,2620,2630,2636,2649,2658,2661,2676,2682,2687,2692,2695,2710,2719,2722,2728,2731,2734,2738,2741,2787,2790,2797,2800,2806,2809,2814,2816,2832,2835,2838,2843,2845,2878,2881,2912,2915,2962,2965,2971,2974,2980,2983,2986,3026,3029,3041,3047,3050,3057,3060,3095,3098,3109,3112,3115,3120,3123,3129,3132,3135,3146,3152,3155,3157,3162,3167,3174,3176,3205,3208,3211,3217,3222,3225,3231,3234,3237,3244,3246,3335,3338,3356,3365,3371,3374,3376,3380,3385,3392,3394,3643,3645,3651,3654,3660,3663,3669,3672,3678,3684,3687,3700,3703,3709,3711,3716,3719,3742,3745,3751,3754,3765,3768,3774,3777,3779,3782,3788,3791,3808,3811,3817,3822,3827,3833,3835,3873,3878,3883,3886,3892,3895,3897,3903,3906,3908,3949,3952,3984,3987,3990,3992,4053,4056,4059,4082,4085,4088,4107,4110,4115,4118,4123,4125,4128,4148,4151,4160,4162,4203,4206,4212,4215,4220,4223,4229,4231,4236,4239,4245,4250,4256,4259,4262,4268,4271,4277,4280,4283,4293,4306,4309,4335,4338,4340,4498,4500,4506,4509,4526,4532,4538,4541,4554,4557,4563,4569,4585,4588,4594,4600,4606,4623,4625,4631,4634,4640,4643,4656,4662,4667,4672,4678,4681,4697,4700,4719,4722,4728,4730,4736,4763,4766,4807,4810,4818,4824,4826,4842,4845,4847,4873,4876,4881,4883,4899,4902,4907,4909,4916,4919,4931,4933,4938,4941,4988,4991,5048,5051,5076,5078,5084,5086,5092,5095,5110,5113,5125,5127,5162,5164,5170,5173,5198,5200,5206,5213,5216,5228,5231,5238,5241,5254,5256,5267,5270,5276,5279,5308,5311,5314,5320,5323,5348,5351,5383,5386,5389,5400,5406,5420,5423,5430,5433,5437,5533,5536,5539,5542,5548,5551,5578,5581,5638],[12,13,14,18],"p",{},[15,16,17],"code",{},"std::atomic\u003CT>"," 用来对一个共享对象执行原子操作，定义在：",[20,21,26],"pre",{"className":22,"code":23,"language":24,"meta":25,"style":25},"language-cpp shiki shiki-themes github-light github-dark","#include \u003Catomic>\n","cpp","",[15,27,28],{"__ignoreMap":25},[29,30,33,37],"span",{"class":31,"line":32},"line",1,[29,34,36],{"class":35},"szBVR","#include",[29,38,40],{"class":39},"sZZnC"," \u003Catomic>\n",[12,42,43],{},"“原子”表示某个操作在并发观察下不可被拆成一半看到。\n例如一个原子自增不会出现另一个线程只看到“读取完成但写回还没完成”的中间状态。",[12,45,46],{},"但要特别注意：",[48,49,50],"blockquote",{},[12,51,52,54],{},[15,53,5],{}," 保证原子语义，不等于保证底层实现一定 lock-free。",[12,56,57],{},"是否真正无锁取决于类型、平台和标准库实现。",[12,59,60,61,64],{},"学习 ",[15,62,63],{},"atomic"," 时不要一上来就想“无锁算法”。",[12,66,67],{},"更适合初学的顺序是：",[20,69,74],{"className":70,"code":72,"language":73,"meta":25},[71],"language-text","普通变量为什么会出错\n      ↓\n单个变量怎样做到原子读写\n      ↓\n读-改-写怎样作为一个整体完成\n      ↓\n多个线程之间怎样发布和观察状态\n      ↓\n什么时候 atomic 不够，需要 mutex\n","text",[15,75,72],{"__ignoreMap":25},[12,77,78,79,81,82,86],{},"也就是说，",[15,80,5],{}," 首先是一个",[83,84,85],"strong",{},"正确性工具","，其次才可能是性能工具。",[88,89,91],"h2",{"id":90},"atomic-解决什么问题","atomic 解决什么问题",[93,94,95],"h3",{"id":95},"为什么普通变量不够",[12,97,98],{},"下面的代码存在数据竞争：",[20,100,102],{"className":22,"code":101,"language":24,"meta":25,"style":25},"int counter = 0;\n\nvoid add_many()\n{\n    for (int i = 0; i \u003C 100000; ++i)\n    {\n        ++counter;\n    }\n}\n",[15,103,104,123,130,143,149,185,191,200,206],{"__ignoreMap":25},[29,105,106,109,113,116,120],{"class":31,"line":32},[29,107,108],{"class":35},"int",[29,110,112],{"class":111},"sVt8B"," counter ",[29,114,115],{"class":35},"=",[29,117,119],{"class":118},"sj4cs"," 0",[29,121,122],{"class":111},";\n",[29,124,126],{"class":31,"line":125},2,[29,127,129],{"emptyLinePlaceholder":128},true,"\n",[29,131,133,136,140],{"class":31,"line":132},3,[29,134,135],{"class":35},"void",[29,137,139],{"class":138},"sScJk"," add_many",[29,141,142],{"class":111},"()\n",[29,144,146],{"class":31,"line":145},4,[29,147,148],{"class":111},"{\n",[29,150,152,155,158,160,163,165,167,170,173,176,179,182],{"class":31,"line":151},5,[29,153,154],{"class":35},"    for",[29,156,157],{"class":111}," (",[29,159,108],{"class":35},[29,161,162],{"class":111}," i ",[29,164,115],{"class":35},[29,166,119],{"class":118},[29,168,169],{"class":111},"; i ",[29,171,172],{"class":35},"\u003C",[29,174,175],{"class":118}," 100000",[29,177,178],{"class":111},"; ",[29,180,181],{"class":35},"++",[29,183,184],{"class":111},"i)\n",[29,186,188],{"class":31,"line":187},6,[29,189,190],{"class":111},"    {\n",[29,192,194,197],{"class":31,"line":193},7,[29,195,196],{"class":35},"        ++",[29,198,199],{"class":111},"counter;\n",[29,201,203],{"class":31,"line":202},8,[29,204,205],{"class":111},"    }\n",[29,207,209],{"class":31,"line":208},9,[29,210,211],{"class":111},"}\n",[12,213,214,215,218],{},"如果多个线程同时执行 ",[15,216,217],{},"++counter","，结果并不可靠。",[12,220,221,222,224],{},"原因不是 ",[15,223,108],{}," 本身不能存数字，而是：",[20,226,228],{"className":22,"code":227,"language":24,"meta":25,"style":25},"++counter;\n",[15,229,230],{"__ignoreMap":25},[29,231,232,234],{"class":31,"line":32},[29,233,181],{"class":35},[29,235,199],{"class":111},[12,237,238],{},"看起来是一句代码，实际通常可以拆成：",[20,240,243],{"className":241,"code":242,"language":73,"meta":25},[71],"读取 counter\n      ↓\n加 1\n      ↓\n写回 counter\n",[15,244,242],{"__ignoreMap":25},[12,246,247],{},"两个线程交错执行时，就可能出现：",[20,249,252],{"className":250,"code":251,"language":73,"meta":25},[71],"线程 A 读到 10\n线程 B 读到 10\n线程 A 写回 11\n线程 B 也写回 11\n",[15,253,251],{"__ignoreMap":25},[12,255,256],{},"明明执行了两次自增，结果只增加了 1。",[12,258,259,260,263],{},"更重要的是，在 C++ 里这不只是“结果偶尔不准”，而是 ",[83,261,262],{},"数据竞争","，属于未定义行为。",[12,265,266],{},"对于这种“单个独立计数器”的情况，可以使用：",[20,268,270],{"className":22,"code":269,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> counter{0};\n",[15,271,272],{"__ignoreMap":25},[29,273,274,277,280,283,286,289],{"class":31,"line":32},[29,275,276],{"class":138},"std",[29,278,279],{"class":111},"::atomic",[29,281,282],{"class":35},"\u003Cint>",[29,284,285],{"class":111}," counter{",[29,287,288],{"class":118},"0",[29,290,291],{"class":111},"};\n",[93,293,295,296,299,300],{"id":294},"最基础的-load-和-store","最基础的 ",[15,297,298],{},"load()"," 和 ",[15,301,302],{},"store()",[12,304,305],{},"原子对象可以显式读取和写入：",[20,307,309],{"className":22,"code":308,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{0};\n\nvalue.store(10);\nint x = value.load();\n",[15,310,311,326,330,347],{"__ignoreMap":25},[29,312,313,315,317,319,322,324],{"class":31,"line":32},[29,314,276],{"class":138},[29,316,279],{"class":111},[29,318,282],{"class":35},[29,320,321],{"class":111}," value{",[29,323,288],{"class":118},[29,325,291],{"class":111},[29,327,328],{"class":31,"line":125},[29,329,129],{"emptyLinePlaceholder":128},[29,331,332,335,338,341,344],{"class":31,"line":132},[29,333,334],{"class":111},"value.",[29,336,337],{"class":138},"store",[29,339,340],{"class":111},"(",[29,342,343],{"class":118},"10",[29,345,346],{"class":111},");\n",[29,348,349,351,354,356,359,362],{"class":31,"line":145},[29,350,108],{"class":35},[29,352,353],{"class":111}," x ",[29,355,115],{"class":35},[29,357,358],{"class":111}," value.",[29,360,361],{"class":138},"load",[29,363,364],{"class":111},"();\n",[12,366,367],{},"很多简单场景也允许写成类似普通变量的形式：",[20,369,371],{"className":22,"code":370,"language":24,"meta":25,"style":25},"value = 10;\nint x = value;\n",[15,372,373,385],{"__ignoreMap":25},[29,374,375,378,380,383],{"class":31,"line":32},[29,376,377],{"class":111},"value ",[29,379,115],{"class":35},[29,381,382],{"class":118}," 10",[29,384,122],{"class":111},[29,386,387,389,391,393],{"class":31,"line":125},[29,388,108],{"class":35},[29,390,353],{"class":111},[29,392,115],{"class":35},[29,394,395],{"class":111}," value;\n",[12,397,398,399,401,402,404],{},"但在学习原子语义时，显式写 ",[15,400,298],{}," \u002F ",[15,403,302],{}," 更容易看出这里发生的是原子访问。",[12,406,407],{},"可以把它们理解成：",[20,409,412],{"className":410,"code":411,"language":73,"meta":25},[71],"load()  ：从 atomic 对象中原子地读出一个值\nstore() ：向 atomic 对象中原子地写入一个值\n",[15,413,411],{"__ignoreMap":25},[12,415,416],{},"这里“原子”只针对这一次读或写。",[12,418,419],{},"例如：",[20,421,423],{"className":22,"code":422,"language":24,"meta":25,"style":25},"int a = value.load();\nint b = other.load();\n",[15,424,425,440],{"__ignoreMap":25},[29,426,427,429,432,434,436,438],{"class":31,"line":32},[29,428,108],{"class":35},[29,430,431],{"class":111}," a ",[29,433,115],{"class":35},[29,435,358],{"class":111},[29,437,361],{"class":138},[29,439,364],{"class":111},[29,441,442,444,447,449,452,454],{"class":31,"line":125},[29,443,108],{"class":35},[29,445,446],{"class":111}," b ",[29,448,115],{"class":35},[29,450,451],{"class":111}," other.",[29,453,361],{"class":138},[29,455,364],{"class":111},[12,457,458,459,461,462,465],{},"这两次 ",[15,460,298],{}," 各自是原子的，但它们并不会自动组成一个“同时读取两个值”的整体事务。\n想要同时读取俩，还是用",[15,463,464],{},"mutex","方便点。",[93,467,469,470],{"id":468},"原子自增与-fetch_add","原子自增与 ",[15,471,472],{},"fetch_add()",[12,474,475,476,479],{},"自增、自减这类操作叫 ",[83,477,478],{},"读-改-写（read-modify-write, RMW）","。",[12,481,482],{},"它们不是简单读，也不是简单写，而是：",[20,484,487],{"className":485,"code":486,"language":73,"meta":25},[71],"读出旧值\n      ↓\n计算新值\n      ↓\n写回新值\n",[15,488,486],{"__ignoreMap":25},[12,490,491,493,494,497],{},[15,492,63],{}," 的关键价值之一，就是能把这",[83,495,496],{},"整个过程","做成一个不可被其他线程插进来的原子操作。",[20,499,501],{"className":22,"code":500,"language":24,"meta":25,"style":25},"#include \u003Catomic>\n#include \u003Cprint>\n#include \u003Cthread>\n#include \u003Cvector>\n\nstd::atomic\u003Cint> counter{0};\n\nvoid add_many()\n{\n    for (int i = 0; i \u003C 100000; ++i)\n    {\n        ++counter;\n    }\n}\n\nint main()\n{\n    std::vector\u003Cstd::thread> threads;\n\n    for (int i = 0; i \u003C 4; ++i)\n    {\n        threads.emplace_back(add_many);\n    }\n\n    for (auto& thread : threads)\n    {\n        thread.join();\n    }\n\n    std::println(\"{}\", counter.load());\n}\n",[15,502,503,509,516,523,530,534,548,552,560,564,591,596,603,608,613,618,628,633,655,660,688,693,705,710,715,728,733,744,749,754,778],{"__ignoreMap":25},[29,504,505,507],{"class":31,"line":32},[29,506,36],{"class":35},[29,508,40],{"class":39},[29,510,511,513],{"class":31,"line":125},[29,512,36],{"class":35},[29,514,515],{"class":39}," \u003Cprint>\n",[29,517,518,520],{"class":31,"line":132},[29,519,36],{"class":35},[29,521,522],{"class":39}," \u003Cthread>\n",[29,524,525,527],{"class":31,"line":145},[29,526,36],{"class":35},[29,528,529],{"class":39}," \u003Cvector>\n",[29,531,532],{"class":31,"line":151},[29,533,129],{"emptyLinePlaceholder":128},[29,535,536,538,540,542,544,546],{"class":31,"line":187},[29,537,276],{"class":138},[29,539,279],{"class":111},[29,541,282],{"class":35},[29,543,285],{"class":111},[29,545,288],{"class":118},[29,547,291],{"class":111},[29,549,550],{"class":31,"line":193},[29,551,129],{"emptyLinePlaceholder":128},[29,553,554,556,558],{"class":31,"line":202},[29,555,135],{"class":35},[29,557,139],{"class":138},[29,559,142],{"class":111},[29,561,562],{"class":31,"line":208},[29,563,148],{"class":111},[29,565,567,569,571,573,575,577,579,581,583,585,587,589],{"class":31,"line":566},10,[29,568,154],{"class":35},[29,570,157],{"class":111},[29,572,108],{"class":35},[29,574,162],{"class":111},[29,576,115],{"class":35},[29,578,119],{"class":118},[29,580,169],{"class":111},[29,582,172],{"class":35},[29,584,175],{"class":118},[29,586,178],{"class":111},[29,588,181],{"class":35},[29,590,184],{"class":111},[29,592,594],{"class":31,"line":593},11,[29,595,190],{"class":111},[29,597,599,601],{"class":31,"line":598},12,[29,600,196],{"class":35},[29,602,199],{"class":111},[29,604,606],{"class":31,"line":605},13,[29,607,205],{"class":111},[29,609,611],{"class":31,"line":610},14,[29,612,211],{"class":111},[29,614,616],{"class":31,"line":615},15,[29,617,129],{"emptyLinePlaceholder":128},[29,619,621,623,626],{"class":31,"line":620},16,[29,622,108],{"class":35},[29,624,625],{"class":138}," main",[29,627,142],{"class":111},[29,629,631],{"class":31,"line":630},17,[29,632,148],{"class":111},[29,634,636,639,642,644,646,649,652],{"class":31,"line":635},18,[29,637,638],{"class":138},"    std",[29,640,641],{"class":111},"::vector",[29,643,172],{"class":35},[29,645,276],{"class":138},[29,647,648],{"class":111},"::thread",[29,650,651],{"class":35},">",[29,653,654],{"class":111}," threads;\n",[29,656,658],{"class":31,"line":657},19,[29,659,129],{"emptyLinePlaceholder":128},[29,661,663,665,667,669,671,673,675,677,679,682,684,686],{"class":31,"line":662},20,[29,664,154],{"class":35},[29,666,157],{"class":111},[29,668,108],{"class":35},[29,670,162],{"class":111},[29,672,115],{"class":35},[29,674,119],{"class":118},[29,676,169],{"class":111},[29,678,172],{"class":35},[29,680,681],{"class":118}," 4",[29,683,178],{"class":111},[29,685,181],{"class":35},[29,687,184],{"class":111},[29,689,691],{"class":31,"line":690},21,[29,692,190],{"class":111},[29,694,696,699,702],{"class":31,"line":695},22,[29,697,698],{"class":111},"        threads.",[29,700,701],{"class":138},"emplace_back",[29,703,704],{"class":111},"(add_many);\n",[29,706,708],{"class":31,"line":707},23,[29,709,205],{"class":111},[29,711,713],{"class":31,"line":712},24,[29,714,129],{"emptyLinePlaceholder":128},[29,716,718,720,722,725],{"class":31,"line":717},25,[29,719,154],{"class":35},[29,721,157],{"class":111},[29,723,724],{"class":35},"auto&",[29,726,727],{"class":111}," thread : threads)\n",[29,729,731],{"class":31,"line":730},26,[29,732,190],{"class":111},[29,734,736,739,742],{"class":31,"line":735},27,[29,737,738],{"class":111},"        thread.",[29,740,741],{"class":138},"join",[29,743,364],{"class":111},[29,745,747],{"class":31,"line":746},28,[29,748,205],{"class":111},[29,750,752],{"class":31,"line":751},29,[29,753,129],{"emptyLinePlaceholder":128},[29,755,757,759,762,765,767,770,773,775],{"class":31,"line":756},30,[29,758,638],{"class":138},[29,760,761],{"class":111},"::",[29,763,764],{"class":138},"println",[29,766,340],{"class":111},[29,768,769],{"class":39},"\"{}\"",[29,771,772],{"class":111},", counter.",[29,774,361],{"class":138},[29,776,777],{"class":111},"());\n",[29,779,781],{"class":31,"line":780},31,[29,782,211],{"class":111},[12,784,785],{},[83,786,787],{},"运行结果：",[20,789,792],{"className":790,"code":791,"language":73,"meta":25},[71],"400000\n",[15,793,791],{"__ignoreMap":25},[12,795,796],{},"还可以显式使用：",[20,798,800],{"className":22,"code":799,"language":24,"meta":25,"style":25},"counter.fetch_add(1);\n",[15,801,802],{"__ignoreMap":25},[29,803,804,807,810,812,815],{"class":31,"line":32},[29,805,806],{"class":111},"counter.",[29,808,809],{"class":138},"fetch_add",[29,811,340],{"class":111},[29,813,814],{"class":118},"1",[29,816,346],{"class":111},[12,818,819],{},"常见原子读改写操作包括：",[20,821,823],{"className":22,"code":822,"language":24,"meta":25,"style":25},"fetch_add()\nfetch_sub()\nfetch_and()\nfetch_or()\nfetch_xor()\n",[15,824,825,831,838,845,852],{"__ignoreMap":25},[29,826,827,829],{"class":31,"line":32},[29,828,809],{"class":138},[29,830,142],{"class":111},[29,832,833,836],{"class":31,"line":125},[29,834,835],{"class":138},"fetch_sub",[29,837,142],{"class":111},[29,839,840,843],{"class":31,"line":132},[29,841,842],{"class":138},"fetch_and",[29,844,142],{"class":111},[29,846,847,850],{"class":31,"line":145},[29,848,849],{"class":138},"fetch_or",[29,851,142],{"class":111},[29,853,854,857],{"class":31,"line":151},[29,855,856],{"class":138},"fetch_xor",[29,858,142],{"class":111},[12,860,861],{},"其中位运算版本主要用于整数原子类型。",[12,863,864,299,866,868],{},[15,865,472],{},[15,867,217],{}," 的区别是：",[20,870,871],{"className":22,"code":227,"language":24,"meta":25,"style":25},[15,872,873],{"__ignoreMap":25},[29,874,875,877],{"class":31,"line":32},[29,876,181],{"class":35},[29,878,199],{"class":111},[12,880,881],{},"更接近“把值加一，并得到加完之后的新值”；",[20,883,884],{"className":22,"code":799,"language":24,"meta":25,"style":25},[15,885,886],{"__ignoreMap":25},[29,887,888,890,892,894,896],{"class":31,"line":32},[29,889,806],{"class":111},[29,891,809],{"class":138},[29,893,340],{"class":111},[29,895,814],{"class":118},[29,897,346],{"class":111},[12,899,900],{},"更明确表达“原子地加 1”，并返回加之前的旧值。",[12,902,419],{},[20,904,906],{"className":22,"code":905,"language":24,"meta":25,"style":25},"int old = counter.fetch_add(1);\n",[15,907,908],{"__ignoreMap":25},[29,909,910,912,915,917,920,922,924,926],{"class":31,"line":32},[29,911,108],{"class":35},[29,913,914],{"class":111}," old ",[29,916,115],{"class":35},[29,918,919],{"class":111}," counter.",[29,921,809],{"class":138},[29,923,340],{"class":111},[29,925,814],{"class":118},[29,927,346],{"class":111},[12,929,930,931,934,935,937,938,479],{},"如果 ",[15,932,933],{},"old == 10","，说明这次操作把 counter 从 ",[15,936,343],{}," 改成了 ",[15,939,940],{},"11",[93,942,944],{"id":943},"exchange",[15,945,946],{},"exchange()",[12,948,949,951],{},[15,950,946],{}," 会原子地：",[953,954,955,959],"ol",{},[956,957,958],"li",{},"写入新值；",[956,960,961],{},"返回旧值。",[20,963,965],{"className":22,"code":964,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{10};\n\nint old = value.exchange(20);\n",[15,966,967,981,985],{"__ignoreMap":25},[29,968,969,971,973,975,977,979],{"class":31,"line":32},[29,970,276],{"class":138},[29,972,279],{"class":111},[29,974,282],{"class":35},[29,976,321],{"class":111},[29,978,343],{"class":118},[29,980,291],{"class":111},[29,982,983],{"class":31,"line":125},[29,984,129],{"emptyLinePlaceholder":128},[29,986,987,989,991,993,995,997,999,1002],{"class":31,"line":132},[29,988,108],{"class":35},[29,990,914],{"class":111},[29,992,115],{"class":35},[29,994,358],{"class":111},[29,996,943],{"class":138},[29,998,340],{"class":111},[29,1000,1001],{"class":118},"20",[29,1003,346],{"class":111},[12,1005,1006],{},"执行后：",[20,1008,1011],{"className":1009,"code":1010,"language":73,"meta":25},[71],"old == 10\nvalue == 20\n",[15,1012,1010],{"__ignoreMap":25},[12,1014,1015],{},"这种“替换并取回旧值”的操作在状态切换里很常见。",[12,1017,1018],{},"例如一个任务状态从：",[20,1020,1023],{"className":1021,"code":1022,"language":73,"meta":25},[71],"idle\n",[15,1024,1022],{"__ignoreMap":25},[12,1026,1027],{},"切换成：",[20,1029,1032],{"className":1030,"code":1031,"language":73,"meta":25},[71],"running\n",[15,1033,1031],{"__ignoreMap":25},[12,1035,1036,1037,479],{},"同时还想知道切换前到底是什么状态，就可以使用 ",[15,1038,946],{},[12,1040,1041],{},"它比：",[20,1043,1045],{"className":22,"code":1044,"language":24,"meta":25,"style":25},"int old = value.load();\nvalue.store(20);\n",[15,1046,1047,1061],{"__ignoreMap":25},[29,1048,1049,1051,1053,1055,1057,1059],{"class":31,"line":32},[29,1050,108],{"class":35},[29,1052,914],{"class":111},[29,1054,115],{"class":35},[29,1056,358],{"class":111},[29,1058,361],{"class":138},[29,1060,364],{"class":111},[29,1062,1063,1065,1067,1069,1071],{"class":31,"line":125},[29,1064,334],{"class":111},[29,1066,337],{"class":138},[29,1068,340],{"class":111},[29,1070,1001],{"class":118},[29,1072,346],{"class":111},[12,1074,1075],{},"更强，因为后者中间可能被其他线程插入修改。",[93,1077,1079,1082],{"id":1078},"stdatomicbool状态标记",[15,1080,1081],{},"std::atomic\u003Cbool>","：状态标记",[20,1084,1086],{"className":22,"code":1085,"language":24,"meta":25,"style":25},"#include \u003Catomic>\n#include \u003Cchrono>\n#include \u003Cprint>\n#include \u003Cthread>\n\nstd::atomic\u003Cbool> running{true};\n\nvoid worker()\n{\n    while (running.load())\n    {\n        std::this_thread::sleep_for(std::chrono::milliseconds(100));\n    }\n\n    std::println(\"stopped\");\n}\n\nint main()\n{\n    std::thread t(worker);\n\n    std::this_thread::sleep_for(std::chrono::milliseconds(350));\n    running.store(false);\n\n    t.join();\n}\n",[15,1087,1088,1094,1101,1107,1113,1117,1134,1138,1147,1151,1164,1168,1205,1209,1213,1228,1232,1236,1244,1248,1261,1265,1296,1310,1314,1323],{"__ignoreMap":25},[29,1089,1090,1092],{"class":31,"line":32},[29,1091,36],{"class":35},[29,1093,40],{"class":39},[29,1095,1096,1098],{"class":31,"line":125},[29,1097,36],{"class":35},[29,1099,1100],{"class":39}," \u003Cchrono>\n",[29,1102,1103,1105],{"class":31,"line":132},[29,1104,36],{"class":35},[29,1106,515],{"class":39},[29,1108,1109,1111],{"class":31,"line":145},[29,1110,36],{"class":35},[29,1112,522],{"class":39},[29,1114,1115],{"class":31,"line":151},[29,1116,129],{"emptyLinePlaceholder":128},[29,1118,1119,1121,1123,1126,1129,1132],{"class":31,"line":187},[29,1120,276],{"class":138},[29,1122,279],{"class":111},[29,1124,1125],{"class":35},"\u003Cbool>",[29,1127,1128],{"class":111}," running{",[29,1130,1131],{"class":118},"true",[29,1133,291],{"class":111},[29,1135,1136],{"class":31,"line":193},[29,1137,129],{"emptyLinePlaceholder":128},[29,1139,1140,1142,1145],{"class":31,"line":202},[29,1141,135],{"class":35},[29,1143,1144],{"class":138}," worker",[29,1146,142],{"class":111},[29,1148,1149],{"class":31,"line":208},[29,1150,148],{"class":111},[29,1152,1153,1156,1159,1161],{"class":31,"line":566},[29,1154,1155],{"class":35},"    while",[29,1157,1158],{"class":111}," (running.",[29,1160,361],{"class":138},[29,1162,1163],{"class":111},"())\n",[29,1165,1166],{"class":31,"line":593},[29,1167,190],{"class":111},[29,1169,1170,1173,1175,1178,1180,1183,1185,1187,1189,1192,1194,1197,1199,1202],{"class":31,"line":598},[29,1171,1172],{"class":138},"        std",[29,1174,761],{"class":111},[29,1176,1177],{"class":138},"this_thread",[29,1179,761],{"class":111},[29,1181,1182],{"class":138},"sleep_for",[29,1184,340],{"class":111},[29,1186,276],{"class":138},[29,1188,761],{"class":111},[29,1190,1191],{"class":138},"chrono",[29,1193,761],{"class":111},[29,1195,1196],{"class":138},"milliseconds",[29,1198,340],{"class":111},[29,1200,1201],{"class":118},"100",[29,1203,1204],{"class":111},"));\n",[29,1206,1207],{"class":31,"line":605},[29,1208,205],{"class":111},[29,1210,1211],{"class":31,"line":610},[29,1212,129],{"emptyLinePlaceholder":128},[29,1214,1215,1217,1219,1221,1223,1226],{"class":31,"line":615},[29,1216,638],{"class":138},[29,1218,761],{"class":111},[29,1220,764],{"class":138},[29,1222,340],{"class":111},[29,1224,1225],{"class":39},"\"stopped\"",[29,1227,346],{"class":111},[29,1229,1230],{"class":31,"line":620},[29,1231,211],{"class":111},[29,1233,1234],{"class":31,"line":630},[29,1235,129],{"emptyLinePlaceholder":128},[29,1237,1238,1240,1242],{"class":31,"line":635},[29,1239,108],{"class":35},[29,1241,625],{"class":138},[29,1243,142],{"class":111},[29,1245,1246],{"class":31,"line":657},[29,1247,148],{"class":111},[29,1249,1250,1252,1255,1258],{"class":31,"line":662},[29,1251,638],{"class":138},[29,1253,1254],{"class":111},"::thread ",[29,1256,1257],{"class":138},"t",[29,1259,1260],{"class":111},"(worker);\n",[29,1262,1263],{"class":31,"line":690},[29,1264,129],{"emptyLinePlaceholder":128},[29,1266,1267,1269,1271,1273,1275,1277,1279,1281,1283,1285,1287,1289,1291,1294],{"class":31,"line":695},[29,1268,638],{"class":138},[29,1270,761],{"class":111},[29,1272,1177],{"class":138},[29,1274,761],{"class":111},[29,1276,1182],{"class":138},[29,1278,340],{"class":111},[29,1280,276],{"class":138},[29,1282,761],{"class":111},[29,1284,1191],{"class":138},[29,1286,761],{"class":111},[29,1288,1196],{"class":138},[29,1290,340],{"class":111},[29,1292,1293],{"class":118},"350",[29,1295,1204],{"class":111},[29,1297,1298,1301,1303,1305,1308],{"class":31,"line":707},[29,1299,1300],{"class":111},"    running.",[29,1302,337],{"class":138},[29,1304,340],{"class":111},[29,1306,1307],{"class":118},"false",[29,1309,346],{"class":111},[29,1311,1312],{"class":31,"line":712},[29,1313,129],{"emptyLinePlaceholder":128},[29,1315,1316,1319,1321],{"class":31,"line":717},[29,1317,1318],{"class":111},"    t.",[29,1320,741],{"class":138},[29,1322,364],{"class":111},[29,1324,1325],{"class":31,"line":730},[29,1326,211],{"class":111},[12,1328,1329],{},[83,1330,787],{},[20,1332,1335],{"className":1333,"code":1334,"language":73,"meta":25},[71],"stopped\n",[15,1336,1334],{"__ignoreMap":25},[12,1338,1339],{},"这种简单停止标记很适合原子变量。",[12,1341,1342,1343,1346],{},"这里的 ",[15,1344,1345],{},"running"," 只表达一个独立事实：",[20,1348,1351],{"className":1349,"code":1350,"language":73,"meta":25},[71],"线程是否继续循环\n",[15,1352,1350],{"__ignoreMap":25},[12,1354,1355,1356,1359],{},"它不需要和其他多个字段一起保持复杂不变量，所以用 ",[15,1357,1358],{},"atomic\u003Cbool>"," 很合适。",[12,1361,1362],{},"如果停止时还要同时修改很多共享状态，例如：",[20,1364,1367],{"className":1365,"code":1366,"language":73,"meta":25},[71],"running = false\nqueue.clear()\nerror_code = xxx\n",[15,1368,1366],{"__ignoreMap":25},[12,1370,1371,1372,1374],{},"那就不能只因为其中一个字段是 atomic，就认为整个停止过程自动安全。此时通常要重新考虑 ",[15,1373,464],{}," 或更完整的停止协议。",[12,1376,1377,1379,1380,1382,1383,479],{},[15,1378,464],{},"可以制造一大段的临界区，而",[15,1381,63],{},"不能，所以这种要好几行临界的就用",[15,1384,464],{},[12,1386,1387,1388,299,1391,1394],{},"但如果使用 C++20，还应该了解后面的 ",[15,1389,1390],{},"std::jthread",[15,1392,1393],{},"std::stop_token","，因为它们提供了更结构化的协作式停止机制。",[93,1396,1398,1400],{"id":1397},"atomic-不等于整个对象线程安全",[15,1399,63],{}," 不等于“整个对象线程安全”",[12,1402,1403],{},"如果一个类有多个字段：",[20,1405,1407],{"className":22,"code":1406,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> x;\nstd::atomic\u003Cint> y;\n",[15,1408,1409,1420],{"__ignoreMap":25},[29,1410,1411,1413,1415,1417],{"class":31,"line":32},[29,1412,276],{"class":138},[29,1414,279],{"class":111},[29,1416,282],{"class":35},[29,1418,1419],{"class":111}," x;\n",[29,1421,1422,1424,1426,1428],{"class":31,"line":125},[29,1423,276],{"class":138},[29,1425,279],{"class":111},[29,1427,282],{"class":35},[29,1429,1430],{"class":111}," y;\n",[12,1432,1433,1434,299,1437,1440],{},"只能保证对 ",[15,1435,1436],{},"x",[15,1438,1439],{},"y"," 各自的原子操作不会撕裂。",[12,1442,1443],{},"它并不会自动保证：",[20,1445,1448],{"className":1446,"code":1447,"language":73,"meta":25},[71],"x 和 y 永远表示同一个逻辑状态\n",[15,1449,1447],{"__ignoreMap":25},[12,1451,419],{},[20,1453,1455],{"className":22,"code":1454,"language":24,"meta":25,"style":25},"x.store(10);\ny.store(20);\n",[15,1456,1457,1470],{"__ignoreMap":25},[29,1458,1459,1462,1464,1466,1468],{"class":31,"line":32},[29,1460,1461],{"class":111},"x.",[29,1463,337],{"class":138},[29,1465,340],{"class":111},[29,1467,343],{"class":118},[29,1469,346],{"class":111},[29,1471,1472,1475,1477,1479,1481],{"class":31,"line":125},[29,1473,1474],{"class":111},"y.",[29,1476,337],{"class":138},[29,1478,340],{"class":111},[29,1480,1001],{"class":118},[29,1482,346],{"class":111},[12,1484,1485],{},"另一个线程完全可能在两次写入之间观察到：",[20,1487,1490],{"className":1488,"code":1489,"language":73,"meta":25},[71],"x == 10\ny == 旧值\n",[15,1491,1489],{"__ignoreMap":25},[12,1493,1494,1495,1497],{},"如果多个变量必须作为整体保持一致，通常应该使用同一把 ",[15,1496,464],{}," 保护整个状态。",[12,1499,1500],{},"可以用一句话记：",[48,1502,1503],{},[12,1504,1505],{},"atomic 保护的是“一个对象的一次操作”，mutex 更适合保护“一组状态的不变量”。",[12,1507,1508],{},"例如银行账户里同时有：",[20,1510,1513],{"className":1511,"code":1512,"language":73,"meta":25},[71],"balance\nlast_update_time\nversion\n",[15,1514,1512],{"__ignoreMap":25},[12,1516,1517],{},"如果这三个值必须一起变化，那么把它们分别做成 atomic 并不一定让设计更清楚。",[88,1519,1521],{"id":1520},"cas-与原子标志","CAS 与原子标志",[93,1523,1525,1528],{"id":1524},"compare_exchange比较并交换cas",[15,1526,1527],{},"compare_exchange","：比较并交换（CAS）",[12,1530,1531],{},"CAS 是很多无锁算法的核心操作。",[12,1533,1534],{},"不过初学时不要先把它想得太神秘。",[12,1536,1537],{},"CAS 最核心的语义就是：",[48,1539,1540],{},[12,1541,1542],{},"只有当值还是我以为的那个旧值时，才把它改成新值。",[12,1544,1545],{},"概念上它做的是：",[20,1547,1550],{"className":1548,"code":1549,"language":73,"meta":25},[71],"如果 atomic 当前值 == expected\n    atomic = desired\n    返回成功\n否则\n    expected = atomic 当前值\n    返回失败\n",[15,1551,1549],{"__ignoreMap":25},[12,1553,1554],{},"C++ 提供：",[20,1556,1558],{"className":22,"code":1557,"language":24,"meta":25,"style":25},"compare_exchange_weak()\ncompare_exchange_strong()\n",[15,1559,1560,1567],{"__ignoreMap":25},[29,1561,1562,1565],{"class":31,"line":32},[29,1563,1564],{"class":138},"compare_exchange_weak",[29,1566,142],{"class":111},[29,1568,1569,1572],{"class":31,"line":125},[29,1570,1571],{"class":138},"compare_exchange_strong",[29,1573,142],{"class":111},[12,1575,1576],{},"示例：",[20,1578,1580],{"className":22,"code":1579,"language":24,"meta":25,"style":25},"#include \u003Catomic>\n#include \u003Cprint>\n\nint main()\n{\n    std::atomic\u003Cint> value{10};\n\n    int expected = 10;\n    bool success = value.compare_exchange_strong(expected, 20);\n\n    std::println(\"{}\", success);\n    std::println(\"{}\", value.load());\n}\n",[15,1581,1582,1588,1594,1598,1606,1610,1624,1628,1642,1663,1667,1682,1701],{"__ignoreMap":25},[29,1583,1584,1586],{"class":31,"line":32},[29,1585,36],{"class":35},[29,1587,40],{"class":39},[29,1589,1590,1592],{"class":31,"line":125},[29,1591,36],{"class":35},[29,1593,515],{"class":39},[29,1595,1596],{"class":31,"line":132},[29,1597,129],{"emptyLinePlaceholder":128},[29,1599,1600,1602,1604],{"class":31,"line":145},[29,1601,108],{"class":35},[29,1603,625],{"class":138},[29,1605,142],{"class":111},[29,1607,1608],{"class":31,"line":151},[29,1609,148],{"class":111},[29,1611,1612,1614,1616,1618,1620,1622],{"class":31,"line":187},[29,1613,638],{"class":138},[29,1615,279],{"class":111},[29,1617,282],{"class":35},[29,1619,321],{"class":111},[29,1621,343],{"class":118},[29,1623,291],{"class":111},[29,1625,1626],{"class":31,"line":193},[29,1627,129],{"emptyLinePlaceholder":128},[29,1629,1630,1633,1636,1638,1640],{"class":31,"line":202},[29,1631,1632],{"class":35},"    int",[29,1634,1635],{"class":111}," expected ",[29,1637,115],{"class":35},[29,1639,382],{"class":118},[29,1641,122],{"class":111},[29,1643,1644,1647,1650,1652,1654,1656,1659,1661],{"class":31,"line":208},[29,1645,1646],{"class":35},"    bool",[29,1648,1649],{"class":111}," success ",[29,1651,115],{"class":35},[29,1653,358],{"class":111},[29,1655,1571],{"class":138},[29,1657,1658],{"class":111},"(expected, ",[29,1660,1001],{"class":118},[29,1662,346],{"class":111},[29,1664,1665],{"class":31,"line":566},[29,1666,129],{"emptyLinePlaceholder":128},[29,1668,1669,1671,1673,1675,1677,1679],{"class":31,"line":593},[29,1670,638],{"class":138},[29,1672,761],{"class":111},[29,1674,764],{"class":138},[29,1676,340],{"class":111},[29,1678,769],{"class":39},[29,1680,1681],{"class":111},", success);\n",[29,1683,1684,1686,1688,1690,1692,1694,1697,1699],{"class":31,"line":598},[29,1685,638],{"class":138},[29,1687,761],{"class":111},[29,1689,764],{"class":138},[29,1691,340],{"class":111},[29,1693,769],{"class":39},[29,1695,1696],{"class":111},", value.",[29,1698,361],{"class":138},[29,1700,777],{"class":111},[29,1702,1703],{"class":31,"line":605},[29,1704,211],{"class":111},[12,1706,1707],{},[83,1708,787],{},[20,1710,1713],{"className":1711,"code":1712,"language":73,"meta":25},[71],"true\n20\n",[15,1714,1712],{"__ignoreMap":25},[12,1716,930,1717,1720],{},[15,1718,1719],{},"expected"," 不匹配：",[20,1722,1724],{"className":22,"code":1723,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{10};\nint expected = 5;\n\nbool success = value.compare_exchange_strong(expected, 20);\n",[15,1725,1726,1740,1753,1757],{"__ignoreMap":25},[29,1727,1728,1730,1732,1734,1736,1738],{"class":31,"line":32},[29,1729,276],{"class":138},[29,1731,279],{"class":111},[29,1733,282],{"class":35},[29,1735,321],{"class":111},[29,1737,343],{"class":118},[29,1739,291],{"class":111},[29,1741,1742,1744,1746,1748,1751],{"class":31,"line":125},[29,1743,108],{"class":35},[29,1745,1635],{"class":111},[29,1747,115],{"class":35},[29,1749,1750],{"class":118}," 5",[29,1752,122],{"class":111},[29,1754,1755],{"class":31,"line":132},[29,1756,129],{"emptyLinePlaceholder":128},[29,1758,1759,1762,1764,1766,1768,1770,1772,1774],{"class":31,"line":145},[29,1760,1761],{"class":35},"bool",[29,1763,1649],{"class":111},[29,1765,115],{"class":35},[29,1767,358],{"class":111},[29,1769,1571],{"class":138},[29,1771,1658],{"class":111},[29,1773,1001],{"class":118},[29,1775,346],{"class":111},[12,1777,1778],{},"那么：",[20,1780,1783],{"className":1781,"code":1782,"language":73,"meta":25},[71],"success == false\nvalue 仍然是 10\nexpected 被更新成 10\n",[15,1784,1782],{"__ignoreMap":25},[12,1786,1787,1788,1790],{},"这里 ",[15,1789,1719],{}," 会被改写，是 CAS 很重要的细节。",[12,1792,1793],{},"它不是单纯的“输入参数”，失败时它会告诉你：",[20,1795,1798],{"className":1796,"code":1797,"language":73,"meta":25},[71],"你猜的旧值不对，当前真实值是这个\n",[15,1799,1797],{"__ignoreMap":25},[12,1801,1802],{},"所以 CAS 循环通常会写成：",[20,1804,1807],{"className":1805,"code":1806,"language":73,"meta":25},[71],"先读当前值作为 expected\n      ↓\n根据 expected 算 desired\n      ↓\n尝试 compare_exchange\n      ↓\n失败时 expected 已被更新，重新计算 desired\n",[15,1808,1806],{"__ignoreMap":25},[1810,1811,1813,299,1816],"h4",{"id":1812},"weak-和-strong",[15,1814,1815],{},"weak",[15,1817,83],{},[12,1819,1820,1823],{},[15,1821,1822],{},"compare_exchange_weak()"," 允许“伪失败”：即使值相等，也可能返回失败。",[12,1825,1826],{},"因此它很适合放在循环中：",[20,1828,1830],{"className":22,"code":1829,"language":24,"meta":25,"style":25},"int expected = value.load();\n\nwhile (!value.compare_exchange_weak(expected, expected + 1))\n{\n}\n",[15,1831,1832,1846,1850,1876,1880],{"__ignoreMap":25},[29,1833,1834,1836,1838,1840,1842,1844],{"class":31,"line":32},[29,1835,108],{"class":35},[29,1837,1635],{"class":111},[29,1839,115],{"class":35},[29,1841,358],{"class":111},[29,1843,361],{"class":138},[29,1845,364],{"class":111},[29,1847,1848],{"class":31,"line":125},[29,1849,129],{"emptyLinePlaceholder":128},[29,1851,1852,1855,1857,1860,1862,1864,1867,1870,1873],{"class":31,"line":132},[29,1853,1854],{"class":35},"while",[29,1856,157],{"class":111},[29,1858,1859],{"class":35},"!",[29,1861,334],{"class":111},[29,1863,1564],{"class":138},[29,1865,1866],{"class":111},"(expected, expected ",[29,1868,1869],{"class":35},"+",[29,1871,1872],{"class":118}," 1",[29,1874,1875],{"class":111},"))\n",[29,1877,1878],{"class":31,"line":145},[29,1879,148],{"class":111},[29,1881,1882],{"class":31,"line":151},[29,1883,211],{"class":111},[12,1885,1886,1887,1889],{},"这段代码能工作，是因为失败时 ",[15,1888,1719],{}," 会被更新成 atomic 当前值。",[12,1891,1892],{},"下一轮循环中的：",[20,1894,1896],{"className":22,"code":1895,"language":24,"meta":25,"style":25},"expected + 1\n",[15,1897,1898],{"__ignoreMap":25},[29,1899,1900,1903,1905],{"class":31,"line":32},[29,1901,1902],{"class":111},"expected ",[29,1904,1869],{"class":35},[29,1906,1907],{"class":118}," 1\n",[12,1909,1910],{},"就会基于更新后的值继续尝试。",[12,1912,1913,1916],{},[15,1914,1915],{},"compare_exchange_strong()"," 不允许这种伪失败，更适合只尝试一次的场景。",[12,1918,1919],{},"简单记法：",[20,1921,1924],{"className":1922,"code":1923,"language":73,"meta":25},[71],"循环里反复尝试\n→ weak 常见\n\n只想尝试一次\n→ strong 更直观\n",[15,1925,1923],{"__ignoreMap":25},[93,1927,1929],{"id":1928},"stdatomic_flag",[15,1930,1931],{},"std::atomic_flag",[12,1933,1934,1936,1937,479],{},[15,1935,1931],{}," 可以理解成一个非常简单的",[83,1938,1939],{},"原子开关",[12,1941,1942],{},"它只有两种状态：",[20,1944,1947],{"className":1945,"code":1946,"language":73,"meta":25},[71],"false：空闲\ntrue ：已占用\n",[15,1948,1946],{"__ignoreMap":25},[12,1950,1951],{},"它常见的两个操作是：",[20,1953,1955],{"className":22,"code":1954,"language":24,"meta":25,"style":25},"test_and_set()\nclear()\n",[15,1956,1957,1964],{"__ignoreMap":25},[29,1958,1959,1962],{"class":31,"line":32},[29,1960,1961],{"class":138},"test_and_set",[29,1963,142],{"class":111},[29,1965,1966,1969],{"class":31,"line":125},[29,1967,1968],{"class":138},"clear",[29,1970,142],{"class":111},[12,1972,1973],{},"其中最关键的是：",[20,1975,1977],{"className":22,"code":1976,"language":24,"meta":25,"style":25},"flag.test_and_set();\n",[15,1978,1979],{"__ignoreMap":25},[29,1980,1981,1984,1986],{"class":31,"line":32},[29,1982,1983],{"class":111},"flag.",[29,1985,1961],{"class":138},[29,1987,364],{"class":111},[12,1989,1990,1991,1994],{},"这个操作会",[83,1992,1993],{},"原子地完成两件事","：",[953,1996,1997,2004],{},[956,1998,1999,2000,2003],{},"返回 ",[15,2001,2002],{},"flag"," 原来的值；",[956,2005,2006,2007,2009,2010,479],{},"同时把 ",[15,2008,2002],{}," 设置为 ",[15,2011,1131],{},[12,2013,419],{},[20,2015,2017],{"className":22,"code":2016,"language":24,"meta":25,"style":25},"std::atomic_flag flag = ATOMIC_FLAG_INIT;\n",[15,2018,2019],{"__ignoreMap":25},[29,2020,2021,2023,2026,2028],{"class":31,"line":32},[29,2022,276],{"class":138},[29,2024,2025],{"class":111},"::atomic_flag flag ",[29,2027,115],{"class":35},[29,2029,2030],{"class":111}," ATOMIC_FLAG_INIT;\n",[12,2032,2033],{},"初始时可以理解为：",[20,2035,2038],{"className":2036,"code":2037,"language":73,"meta":25},[71],"flag = false\n",[15,2039,2037],{"__ignoreMap":25},[12,2041,2042],{},"第一次执行：",[20,2044,2046],{"className":22,"code":2045,"language":24,"meta":25,"style":25},"bool old = flag.test_and_set();\n",[15,2047,2048],{"__ignoreMap":25},[29,2049,2050,2052,2054,2056,2059,2061],{"class":31,"line":32},[29,2051,1761],{"class":35},[29,2053,914],{"class":111},[29,2055,115],{"class":35},[29,2057,2058],{"class":111}," flag.",[29,2060,1961],{"class":138},[29,2062,364],{"class":111},[12,2064,2065],{},"结果相当于：",[20,2067,2070],{"className":2068,"code":2069,"language":73,"meta":25},[71],"old = false\nflag = true\n",[15,2071,2069],{"__ignoreMap":25},[12,2073,2074],{},"如果这时候另一个线程再次执行：",[20,2076,2077],{"className":22,"code":2045,"language":24,"meta":25,"style":25},[15,2078,2079],{"__ignoreMap":25},[29,2080,2081,2083,2085,2087,2089,2091],{"class":31,"line":32},[29,2082,1761],{"class":35},[29,2084,914],{"class":111},[29,2086,115],{"class":35},[29,2088,2058],{"class":111},[29,2090,1961],{"class":138},[29,2092,364],{"class":111},[12,2094,2095],{},"那么结果就是：",[20,2097,2100],{"className":2098,"code":2099,"language":73,"meta":25},[71],"old = true\nflag = true\n",[15,2101,2099],{"__ignoreMap":25},[12,2103,2104],{},"因此，可以利用它实现一个最简单的自旋锁：",[20,2106,2108],{"className":22,"code":2107,"language":24,"meta":25,"style":25},"std::atomic_flag flag = ATOMIC_FLAG_INIT;\n\nwhile (flag.test_and_set())\n{\n    \u002F\u002F 自旋等待\n}\n\n\u002F\u002F 临界区\n\nflag.clear();\n",[15,2109,2110,2120,2124,2135,2139,2145,2149,2153,2158,2162],{"__ignoreMap":25},[29,2111,2112,2114,2116,2118],{"class":31,"line":32},[29,2113,276],{"class":138},[29,2115,2025],{"class":111},[29,2117,115],{"class":35},[29,2119,2030],{"class":111},[29,2121,2122],{"class":31,"line":125},[29,2123,129],{"emptyLinePlaceholder":128},[29,2125,2126,2128,2131,2133],{"class":31,"line":132},[29,2127,1854],{"class":35},[29,2129,2130],{"class":111}," (flag.",[29,2132,1961],{"class":138},[29,2134,1163],{"class":111},[29,2136,2137],{"class":31,"line":145},[29,2138,148],{"class":111},[29,2140,2141],{"class":31,"line":151},[29,2142,2144],{"class":2143},"sJ8bj","    \u002F\u002F 自旋等待\n",[29,2146,2147],{"class":31,"line":187},[29,2148,211],{"class":111},[29,2150,2151],{"class":31,"line":193},[29,2152,129],{"emptyLinePlaceholder":128},[29,2154,2155],{"class":31,"line":202},[29,2156,2157],{"class":2143},"\u002F\u002F 临界区\n",[29,2159,2160],{"class":31,"line":208},[29,2161,129],{"emptyLinePlaceholder":128},[29,2163,2164,2166,2168],{"class":31,"line":566},[29,2165,1983],{"class":111},[29,2167,1968],{"class":138},[29,2169,364],{"class":111},[12,2171,2172],{},"这段代码的逻辑是：",[20,2174,2177],{"className":2175,"code":2176,"language":73,"meta":25},[71],"flag == false\n    ↓\n线程执行 test_and_set()\n    ↓\n返回 false，同时把 flag 改为 true\n    ↓\nwhile(false)\n    ↓\n成功进入临界区\n",[15,2178,2176],{"__ignoreMap":25},[12,2180,2181],{},"如果另一个线程这时候也来执行：",[20,2183,2184],{"className":22,"code":1976,"language":24,"meta":25,"style":25},[15,2185,2186],{"__ignoreMap":25},[29,2187,2188,2190,2192],{"class":31,"line":32},[29,2189,1983],{"class":111},[29,2191,1961],{"class":138},[29,2193,364],{"class":111},[12,2195,2196,2197,2199,2200,2202,2203,1994],{},"由于 ",[15,2198,2002],{}," 已经是 ",[15,2201,1131],{},"，所以它会返回 ",[15,2204,1131],{},[20,2206,2208],{"className":22,"code":2207,"language":24,"meta":25,"style":25},"while (true)\n{\n}\n",[15,2209,2210,2221,2225],{"__ignoreMap":25},[29,2211,2212,2214,2216,2218],{"class":31,"line":32},[29,2213,1854],{"class":35},[29,2215,157],{"class":111},[29,2217,1131],{"class":118},[29,2219,2220],{"class":111},")\n",[29,2222,2223],{"class":31,"line":125},[29,2224,148],{"class":111},[29,2226,2227],{"class":31,"line":132},[29,2228,211],{"class":111},[12,2230,2231],{},"于是这个线程会不断重复执行：",[20,2233,2235],{"className":22,"code":2234,"language":24,"meta":25,"style":25},"flag.test_and_set();\nflag.test_and_set();\nflag.test_and_set();\n...\n",[15,2236,2237,2245,2253,2261],{"__ignoreMap":25},[29,2238,2239,2241,2243],{"class":31,"line":32},[29,2240,1983],{"class":111},[29,2242,1961],{"class":138},[29,2244,364],{"class":111},[29,2246,2247,2249,2251],{"class":31,"line":125},[29,2248,1983],{"class":111},[29,2250,1961],{"class":138},[29,2252,364],{"class":111},[29,2254,2255,2257,2259],{"class":31,"line":132},[29,2256,1983],{"class":111},[29,2258,1961],{"class":138},[29,2260,364],{"class":111},[29,2262,2263],{"class":31,"line":145},[29,2264,2265],{"class":111},"...\n",[12,2267,2268],{},"直到持有锁的线程执行：",[20,2270,2272],{"className":22,"code":2271,"language":24,"meta":25,"style":25},"flag.clear();\n",[15,2273,2274],{"__ignoreMap":25},[29,2275,2276,2278,2280],{"class":31,"line":32},[29,2277,1983],{"class":111},[29,2279,1968],{"class":138},[29,2281,364],{"class":111},[12,2283,2284],{},"把：",[20,2286,2289],{"className":2287,"code":2288,"language":73,"meta":25},[71],"flag = true\n",[15,2290,2288],{"__ignoreMap":25},[12,2292,2293],{},"重新变成：",[20,2295,2297],{"className":2296,"code":2037,"language":73,"meta":25},[71],[15,2298,2037],{"__ignoreMap":25},[12,2300,2301,2302,2305],{},"等待中的线程下一次执行 ",[15,2303,2304],{},"test_and_set()"," 时，就可以成功获得这个“锁”。",[12,2307,2308],{},"这种不断循环检查锁有没有释放的行为，就叫做：",[20,2310,2313],{"className":2311,"code":2312,"language":73,"meta":25},[71],"自旋（spin）\n",[15,2314,2312],{"__ignoreMap":25},[12,2316,2317],{},"可以把它理解成：",[48,2319,2320],{},[12,2321,2322],{},"一个线程发现锁被别人占用了之后，不睡眠，也不阻塞，而是一直站在那里反复询问：“锁释放了吗？”",[12,2324,2325,2326,2329],{},"所以自旋锁和普通 ",[15,2327,2328],{},"std::mutex"," 的区别之一就在这里。",[12,2331,2332],{},"普通互斥量在等待时间较长时，线程通常可以被操作系统阻塞或挂起，避免持续占用 CPU。",[12,2334,2335],{},"而自旋锁：",[20,2337,2339],{"className":22,"code":2338,"language":24,"meta":25,"style":25},"while (flag.test_and_set())\n{\n}\n",[15,2340,2341,2351,2355],{"__ignoreMap":25},[29,2342,2343,2345,2347,2349],{"class":31,"line":32},[29,2344,1854],{"class":35},[29,2346,2130],{"class":111},[29,2348,1961],{"class":138},[29,2350,1163],{"class":111},[29,2352,2353],{"class":31,"line":125},[29,2354,148],{"class":111},[29,2356,2357],{"class":31,"line":132},[29,2358,211],{"class":111},[12,2360,2361],{},"会一直执行循环，因此可能持续占用 CPU。",[12,2363,419],{},[20,2365,2367],{"className":22,"code":2366,"language":24,"meta":25,"style":25},"while (flag.test_and_set())\n{\n}\n\n\u002F\u002F 临界区\n\nread_file();\nsend_network();\nsleep();\ndo_heavy_work();\n\nflag.clear();\n",[15,2368,2369,2379,2383,2387,2391,2395,2399,2406,2413,2420,2427,2431],{"__ignoreMap":25},[29,2370,2371,2373,2375,2377],{"class":31,"line":32},[29,2372,1854],{"class":35},[29,2374,2130],{"class":111},[29,2376,1961],{"class":138},[29,2378,1163],{"class":111},[29,2380,2381],{"class":31,"line":125},[29,2382,148],{"class":111},[29,2384,2385],{"class":31,"line":132},[29,2386,211],{"class":111},[29,2388,2389],{"class":31,"line":145},[29,2390,129],{"emptyLinePlaceholder":128},[29,2392,2393],{"class":31,"line":151},[29,2394,2157],{"class":2143},[29,2396,2397],{"class":31,"line":187},[29,2398,129],{"emptyLinePlaceholder":128},[29,2400,2401,2404],{"class":31,"line":193},[29,2402,2403],{"class":138},"read_file",[29,2405,364],{"class":111},[29,2407,2408,2411],{"class":31,"line":202},[29,2409,2410],{"class":138},"send_network",[29,2412,364],{"class":111},[29,2414,2415,2418],{"class":31,"line":208},[29,2416,2417],{"class":138},"sleep",[29,2419,364],{"class":111},[29,2421,2422,2425],{"class":31,"line":566},[29,2423,2424],{"class":138},"do_heavy_work",[29,2426,364],{"class":111},[29,2428,2429],{"class":31,"line":593},[29,2430,129],{"emptyLinePlaceholder":128},[29,2432,2433,2435,2437],{"class":31,"line":598},[29,2434,1983],{"class":111},[29,2436,1968],{"class":138},[29,2438,364],{"class":111},[12,2440,2441],{},"如果临界区执行时间很长，其他线程就会一直在外面自旋等待，白白浪费 CPU。",[12,2443,2444],{},"因此，自旋锁更适合临界区非常短的情况，例如：",[20,2446,2448],{"className":22,"code":2447,"language":24,"meta":25,"style":25},"while (flag.test_and_set())\n{\n}\n\n++value;\n\nflag.clear();\n",[15,2449,2450,2460,2464,2468,2472,2479,2483],{"__ignoreMap":25},[29,2451,2452,2454,2456,2458],{"class":31,"line":32},[29,2453,1854],{"class":35},[29,2455,2130],{"class":111},[29,2457,1961],{"class":138},[29,2459,1163],{"class":111},[29,2461,2462],{"class":31,"line":125},[29,2463,148],{"class":111},[29,2465,2466],{"class":31,"line":132},[29,2467,211],{"class":111},[29,2469,2470],{"class":31,"line":145},[29,2471,129],{"emptyLinePlaceholder":128},[29,2473,2474,2476],{"class":31,"line":151},[29,2475,181],{"class":35},[29,2477,2478],{"class":111},"value;\n",[29,2480,2481],{"class":31,"line":187},[29,2482,129],{"emptyLinePlaceholder":128},[29,2484,2485,2487,2489],{"class":31,"line":193},[29,2486,1983],{"class":111},[29,2488,1968],{"class":138},[29,2490,364],{"class":111},[12,2492,2493],{},"如果锁很快就会被释放，那么线程稍微自旋几次，可能比让操作系统把线程挂起、再重新唤醒的开销更低。",[12,2495,2496,2497,2499],{},"但在普通业务代码中，一般不要因为自旋锁“看起来简单”就自己使用 ",[15,2498,1931],{}," 实现锁。",[12,2501,2502],{},"如果临界区中存在：",[20,2504,2507],{"className":2505,"code":2506,"language":73,"meta":25},[71],"IO\n\nsleep\n\n网络请求\n\n文件操作\n\n复杂计算\n\n可能阻塞的函数\n",[15,2508,2506],{"__ignoreMap":25},[12,2510,2511],{},"通常更适合使用：",[20,2513,2515],{"className":22,"code":2514,"language":24,"meta":25,"style":25},"std::mutex\n",[15,2516,2517],{"__ignoreMap":25},[29,2518,2519,2521],{"class":31,"line":32},[29,2520,276],{"class":138},[29,2522,2523],{"class":111},"::mutex\n",[12,2525,2526],{},"配合：",[20,2528,2530],{"className":22,"code":2529,"language":24,"meta":25,"style":25},"std::lock_guard\nstd::scoped_lock\nstd::unique_lock\n",[15,2531,2532,2539,2546],{"__ignoreMap":25},[29,2533,2534,2536],{"class":31,"line":32},[29,2535,276],{"class":138},[29,2537,2538],{"class":111},"::lock_guard\n",[29,2540,2541,2543],{"class":31,"line":125},[29,2542,276],{"class":138},[29,2544,2545],{"class":111},"::scoped_lock\n",[29,2547,2548,2550],{"class":31,"line":132},[29,2549,276],{"class":138},[29,2551,2552],{"class":111},"::unique_lock\n",[12,2554,2555],{},"来进行同步。",[12,2557,2558,2560],{},[15,2559,1931],{}," 真正重要的地方在于：",[20,2562,2564],{"className":22,"code":2563,"language":24,"meta":25,"style":25},"test_and_set()\n",[15,2565,2566],{"__ignoreMap":25},[29,2567,2568,2570],{"class":31,"line":32},[29,2569,1961],{"class":138},[29,2571,142],{"class":111},[12,2573,2574],{},"中的：",[20,2576,2579],{"className":2577,"code":2578,"language":73,"meta":25},[71],"读取旧值 + 设置为 true\n",[15,2580,2578],{"__ignoreMap":25},[12,2582,2583],{},"是一个不可分割的原子操作。",[12,2585,2586,2587,1994],{},"如果使用普通 ",[15,2588,1761],{},[20,2590,2592],{"className":22,"code":2591,"language":24,"meta":25,"style":25},"bool flag = false;\n",[15,2593,2594],{"__ignoreMap":25},[29,2595,2596,2598,2601,2603,2606],{"class":31,"line":32},[29,2597,1761],{"class":35},[29,2599,2600],{"class":111}," flag ",[29,2602,115],{"class":35},[29,2604,2605],{"class":118}," false",[29,2607,122],{"class":111},[12,2609,2610],{},"两个线程可能同时发生：",[20,2612,2615],{"className":2613,"code":2614,"language":73,"meta":25},[71],"线程 A：看到 flag == false\n线程 B：也看到 flag == false\n\n线程 A：flag = true\n线程 B：flag = true\n\n线程 A：认为自己获得了锁\n线程 B：也认为自己获得了锁\n",[15,2616,2614],{"__ignoreMap":25},[12,2618,2619],{},"这样两个线程就可能同时进入临界区。",[12,2621,2622,2623,2626,2627,2629],{},"而 ",[15,2624,2625],{},"std::atomic_flag::test_and_set()"," 可以保证多个线程同时竞争时，只有一个线程能够看到原来的值是 ",[15,2628,1307],{},"，从而成功进入临界区。",[12,2631,2632,2633,2635],{},"因此，可以把 ",[15,2634,1931],{}," 简单理解为：",[48,2637,2638],{},[12,2639,2640,2642,2643,2645,2646,2648],{},[15,2641,1931],{}," 是一个原子的“占用标志”。",[15,2644,2304],{}," 会原子地读取旧值并把标志设置为 ",[15,2647,1131],{},"，因此可以用它实现最简单的自旋锁；没有抢到锁的线程会不断循环检查，这种行为就叫自旋。",[93,2650,2652,2655,2656],{"id":2651},"volatile-不能替代-atomic",[15,2653,2654],{},"volatile"," 不能替代 ",[15,2657,63],{},[12,2659,2660],{},"这是非常常见的误区。",[20,2662,2664],{"className":22,"code":2663,"language":24,"meta":25,"style":25},"volatile bool running;\n",[15,2665,2666],{"__ignoreMap":25},[29,2667,2668,2670,2673],{"class":31,"line":32},[29,2669,2654],{"class":35},[29,2671,2672],{"class":35}," bool",[29,2674,2675],{"class":111}," running;\n",[12,2677,2678,2681],{},[83,2679,2680],{},"不能","用来解决 C++ 多线程同步问题。",[12,2683,2684,2686],{},[15,2685,2654],{}," 主要表达的是：",[48,2688,2689],{},[12,2690,2691],{},"这个对象的访问具有特殊可观察意义，编译器不能像普通对象那样随意消除相关访问。",[12,2693,2694],{},"它不提供：",[2696,2697,2698,2701,2704,2707],"ul",{},[956,2699,2700],{},"原子性；",[956,2702,2703],{},"跨线程同步；",[956,2705,2706],{},"happens-before 关系；",[956,2708,2709],{},"数据竞争保护。",[12,2711,2712,2713,2715,2716,2718],{},"线程间共享状态应使用 ",[15,2714,5],{},"、",[15,2717,464],{}," 或其他标准同步工具。",[12,2720,2721],{},"可以粗略记成：",[20,2723,2726],{"className":2724,"code":2725,"language":73,"meta":25},[71],"volatile：主要解决“编译器不要省略这次访问”\natomic  ：解决“多线程下这次访问如何正确同步”\n",[15,2727,2725],{"__ignoreMap":25},[12,2729,2730],{},"它们不是同一类工具。",[88,2732,2733],{"id":2733},"内存序",[93,2735,2737],{"id":2736},"为什么-atomic-还有第二个参数","为什么 atomic 还有第二个参数",[12,2739,2740],{},"你会看到：",[20,2742,2744],{"className":22,"code":2743,"language":24,"meta":25,"style":25},"value.store(10, std::memory_order_release);\n\nint x = value.load(std::memory_order_acquire);\n",[15,2745,2746,2764,2768],{"__ignoreMap":25},[29,2747,2748,2750,2752,2754,2756,2759,2761],{"class":31,"line":32},[29,2749,334],{"class":111},[29,2751,337],{"class":138},[29,2753,340],{"class":111},[29,2755,343],{"class":118},[29,2757,2758],{"class":111},", ",[29,2760,276],{"class":138},[29,2762,2763],{"class":111},"::memory_order_release);\n",[29,2765,2766],{"class":31,"line":125},[29,2767,129],{"emptyLinePlaceholder":128},[29,2769,2770,2772,2774,2776,2778,2780,2782,2784],{"class":31,"line":132},[29,2771,108],{"class":35},[29,2773,353],{"class":111},[29,2775,115],{"class":35},[29,2777,358],{"class":111},[29,2779,361],{"class":138},[29,2781,340],{"class":111},[29,2783,276],{"class":138},[29,2785,2786],{"class":111},"::memory_order_acquire);\n",[12,2788,2789],{},"这里的第二个参数控制的是：",[48,2791,2792],{},[12,2793,2794],{},[83,2795,2796],{},"内存顺序（memory ordering）",[12,2798,2799],{},"学习内存序时，最重要的是先区分两个概念：",[20,2801,2804],{"className":2802,"code":2803,"language":73,"meta":25},[71],"原子性\n\n和\n\n内存访问之间的顺序\n",[15,2805,2803],{"__ignoreMap":25},[12,2807,2808],{},"原子性解决的是：",[48,2810,2811],{},[12,2812,2813],{},"这个 atomic 对象本身的操作是不是不可分割的？",[12,2815,419],{},[20,2817,2818],{"className":22,"code":799,"language":24,"meta":25,"style":25},[15,2819,2820],{"__ignoreMap":25},[29,2821,2822,2824,2826,2828,2830],{"class":31,"line":32},[29,2823,806],{"class":111},[29,2825,809],{"class":138},[29,2827,340],{"class":111},[29,2829,814],{"class":118},[29,2831,346],{"class":111},[12,2833,2834],{},"多个线程同时执行时，不会因为普通的“读取 → 加一 → 写回”互相覆盖而把计数加丢。",[12,2836,2837],{},"而内存序解决的是：",[48,2839,2840],{},[12,2841,2842],{},"这个 atomic 操作前后的其他内存读写，应该和其他线程建立什么样的顺序关系？",[12,2844,419],{},[20,2846,2848],{"className":22,"code":2847,"language":24,"meta":25,"style":25},"int data = 0;\nstd::atomic\u003Cbool> ready{false};\n",[15,2849,2850,2863],{"__ignoreMap":25},[29,2851,2852,2854,2857,2859,2861],{"class":31,"line":32},[29,2853,108],{"class":35},[29,2855,2856],{"class":111}," data ",[29,2858,115],{"class":35},[29,2860,119],{"class":118},[29,2862,122],{"class":111},[29,2864,2865,2867,2869,2871,2874,2876],{"class":31,"line":125},[29,2866,276],{"class":138},[29,2868,279],{"class":111},[29,2870,1125],{"class":35},[29,2872,2873],{"class":111}," ready{",[29,2875,1307],{"class":118},[29,2877,291],{"class":111},[12,2879,2880],{},"线程 A：",[20,2882,2884],{"className":22,"code":2883,"language":24,"meta":25,"style":25},"data = 42;\nready.store(true, ...);\n",[15,2885,2886,2898],{"__ignoreMap":25},[29,2887,2888,2891,2893,2896],{"class":31,"line":32},[29,2889,2890],{"class":111},"data ",[29,2892,115],{"class":35},[29,2894,2895],{"class":118}," 42",[29,2897,122],{"class":111},[29,2899,2900,2903,2905,2907,2909],{"class":31,"line":125},[29,2901,2902],{"class":111},"ready.",[29,2904,337],{"class":138},[29,2906,340],{"class":111},[29,2908,1131],{"class":118},[29,2910,2911],{"class":111},", ...);\n",[12,2913,2914],{},"线程 B：",[20,2916,2918],{"className":22,"code":2917,"language":24,"meta":25,"style":25},"while (!ready.load(...))\n{\n}\n\nstd::println(\"{}\", data);\n",[15,2919,2920,2935,2939,2943,2947],{"__ignoreMap":25},[29,2921,2922,2924,2926,2928,2930,2932],{"class":31,"line":32},[29,2923,1854],{"class":35},[29,2925,157],{"class":111},[29,2927,1859],{"class":35},[29,2929,2902],{"class":111},[29,2931,361],{"class":138},[29,2933,2934],{"class":111},"(...))\n",[29,2936,2937],{"class":31,"line":125},[29,2938,148],{"class":111},[29,2940,2941],{"class":31,"line":132},[29,2942,211],{"class":111},[29,2944,2945],{"class":31,"line":145},[29,2946,129],{"emptyLinePlaceholder":128},[29,2948,2949,2951,2953,2955,2957,2959],{"class":31,"line":151},[29,2950,276],{"class":138},[29,2952,761],{"class":111},[29,2954,764],{"class":138},[29,2956,340],{"class":111},[29,2958,769],{"class":39},[29,2960,2961],{"class":111},", data);\n",[12,2963,2964],{},"这里我们不只关心：",[20,2966,2969],{"className":2967,"code":2968,"language":73,"meta":25},[71],"ready 能不能正确读到 true\n",[15,2970,2968],{"__ignoreMap":25},[12,2972,2973],{},"还关心：",[20,2975,2978],{"className":2976,"code":2977,"language":73,"meta":25},[71],"线程 B 看到 ready == true 以后，\n\n能不能保证看到线程 A 之前写好的 data = 42。\n",[15,2979,2977],{"__ignoreMap":25},[12,2981,2982],{},"这就是内存序要解决的问题。",[12,2984,2985],{},"C++ 常见的内存序包括：",[20,2987,2989],{"className":22,"code":2988,"language":24,"meta":25,"style":25},"std::memory_order_relaxed\nstd::memory_order_acquire\nstd::memory_order_release\nstd::memory_order_acq_rel\nstd::memory_order_seq_cst\n",[15,2990,2991,2998,3005,3012,3019],{"__ignoreMap":25},[29,2992,2993,2995],{"class":31,"line":32},[29,2994,276],{"class":138},[29,2996,2997],{"class":111},"::memory_order_relaxed\n",[29,2999,3000,3002],{"class":31,"line":125},[29,3001,276],{"class":138},[29,3003,3004],{"class":111},"::memory_order_acquire\n",[29,3006,3007,3009],{"class":31,"line":132},[29,3008,276],{"class":138},[29,3010,3011],{"class":111},"::memory_order_release\n",[29,3013,3014,3016],{"class":31,"line":145},[29,3015,276],{"class":138},[29,3017,3018],{"class":111},"::memory_order_acq_rel\n",[29,3020,3021,3023],{"class":31,"line":151},[29,3022,276],{"class":138},[29,3024,3025],{"class":111},"::memory_order_seq_cst\n",[12,3027,3028],{},"还有：",[20,3030,3032],{"className":22,"code":3031,"language":24,"meta":25,"style":25},"std::memory_order_consume\n",[15,3033,3034],{"__ignoreMap":25},[29,3035,3036,3038],{"class":31,"line":32},[29,3037,276],{"class":138},[29,3039,3040],{"class":111},"::memory_order_consume\n",[12,3042,3043,3044,479],{},"但实际工程中通常不要主动使用 ",[15,3045,3046],{},"consume",[3048,3049],"hr",{},[93,3051,3053,3054],{"id":3052},"默认内存序seq_cst","默认内存序：",[15,3055,3056],{},"seq_cst",[12,3058,3059],{},"如果不写第二个参数：",[20,3061,3063],{"className":22,"code":3062,"language":24,"meta":25,"style":25},"value.store(10);\n\nint x = value.load();\n",[15,3064,3065,3077,3081],{"__ignoreMap":25},[29,3066,3067,3069,3071,3073,3075],{"class":31,"line":32},[29,3068,334],{"class":111},[29,3070,337],{"class":138},[29,3072,340],{"class":111},[29,3074,343],{"class":118},[29,3076,346],{"class":111},[29,3078,3079],{"class":31,"line":125},[29,3080,129],{"emptyLinePlaceholder":128},[29,3082,3083,3085,3087,3089,3091,3093],{"class":31,"line":132},[29,3084,108],{"class":35},[29,3086,353],{"class":111},[29,3088,115],{"class":35},[29,3090,358],{"class":111},[29,3092,361],{"class":138},[29,3094,364],{"class":111},[12,3096,3097],{},"默认使用：",[20,3099,3101],{"className":22,"code":3100,"language":24,"meta":25,"style":25},"std::memory_order_seq_cst\n",[15,3102,3103],{"__ignoreMap":25},[29,3104,3105,3107],{"class":31,"line":32},[29,3106,276],{"class":138},[29,3108,3025],{"class":111},[12,3110,3111],{},"它是最强、也最容易理解的常用内存序。",[12,3113,3114],{},"可以先粗略理解成：",[48,3116,3117],{},[12,3118,3119],{},"所有线程看到这些原子操作时，效果尽量像它们都处在一个大家一致认可的全局顺序中。",[12,3121,3122],{},"所以初学阶段：",[20,3124,3127],{"className":3125,"code":3126,"language":73,"meta":25},[71],"不知道应该用什么\n→ 直接使用默认的 seq_cst\n",[15,3128,3126],{"__ignoreMap":25},[12,3130,3131],{},"通常是最稳妥的选择。",[12,3133,3134],{},"不要为了所谓“性能优化”，看到：",[20,3136,3138],{"className":22,"code":3137,"language":24,"meta":25,"style":25},"std::memory_order_relaxed\n",[15,3139,3140],{"__ignoreMap":25},[29,3141,3142,3144],{"class":31,"line":32},[29,3143,276],{"class":138},[29,3145,2997],{"class":111},[12,3147,3148,3149,479],{},"就把所有原子操作都改成 ",[15,3150,3151],{},"relaxed",[12,3153,3154],{},"内存序写错以后，程序可能平时运行完全正常，但换个平台、编译器或优化级别以后才出现非常难排查的并发问题。",[3048,3156],{},[93,3158,3160],{"id":3159},"memory_order_relaxed",[15,3161,3159],{},[12,3163,3164,3166],{},[15,3165,3151],{}," 只要求：",[48,3168,3169],{},[12,3170,3171],{},[83,3172,3173],{},"这个 atomic 对象本身的操作仍然是原子的。",[12,3175,419],{},[20,3177,3179],{"className":22,"code":3178,"language":24,"meta":25,"style":25},"counter.fetch_add(\n    1,\n    std::memory_order_relaxed);\n",[15,3180,3181,3190,3198],{"__ignoreMap":25},[29,3182,3183,3185,3187],{"class":31,"line":32},[29,3184,806],{"class":111},[29,3186,809],{"class":138},[29,3188,3189],{"class":111},"(\n",[29,3191,3192,3195],{"class":31,"line":125},[29,3193,3194],{"class":118},"    1",[29,3196,3197],{"class":111},",\n",[29,3199,3200,3202],{"class":31,"line":132},[29,3201,638],{"class":138},[29,3203,3204],{"class":111},"::memory_order_relaxed);\n",[12,3206,3207],{},"多个线程同时执行时，计数仍然不会因为并发更新而丢失。",[12,3209,3210],{},"所以像这种：",[20,3212,3215],{"className":3213,"code":3214,"language":73,"meta":25},[71],"独立统计次数\n\n请求计数\n\n事件发生次数\n",[15,3216,3214],{"__ignoreMap":25},[12,3218,3219,3220,479],{},"通常可以考虑 ",[15,3221,3151],{},[12,3223,3224],{},"因为我们只关心：",[20,3226,3229],{"className":3227,"code":3228,"language":73,"meta":25},[71],"counter 自己最后是多少\n",[15,3230,3228],{"__ignoreMap":25},[12,3232,3233],{},"并不需要通过这个 counter 去同步其他普通变量。",[12,3235,3236],{},"但要注意：",[48,3238,3239],{},[12,3240,3241,3243],{},[15,3242,3151],{}," 只保证 atomic 自己的访问，不会顺便让旁边的普通变量变得线程安全。",[12,3245,419],{},[20,3247,3249],{"className":22,"code":3248,"language":24,"meta":25,"style":25},"int data = 0;\nstd::atomic\u003Cbool> ready{false};\n\nvoid producer()\n{\n    data = 42;\n\n    ready.store(\n        true,\n        std::memory_order_relaxed);\n}\n",[15,3250,3251,3263,3277,3281,3290,3294,3305,3309,3318,3325,3331],{"__ignoreMap":25},[29,3252,3253,3255,3257,3259,3261],{"class":31,"line":32},[29,3254,108],{"class":35},[29,3256,2856],{"class":111},[29,3258,115],{"class":35},[29,3260,119],{"class":118},[29,3262,122],{"class":111},[29,3264,3265,3267,3269,3271,3273,3275],{"class":31,"line":125},[29,3266,276],{"class":138},[29,3268,279],{"class":111},[29,3270,1125],{"class":35},[29,3272,2873],{"class":111},[29,3274,1307],{"class":118},[29,3276,291],{"class":111},[29,3278,3279],{"class":31,"line":132},[29,3280,129],{"emptyLinePlaceholder":128},[29,3282,3283,3285,3288],{"class":31,"line":145},[29,3284,135],{"class":35},[29,3286,3287],{"class":138}," producer",[29,3289,142],{"class":111},[29,3291,3292],{"class":31,"line":151},[29,3293,148],{"class":111},[29,3295,3296,3299,3301,3303],{"class":31,"line":187},[29,3297,3298],{"class":111},"    data ",[29,3300,115],{"class":35},[29,3302,2895],{"class":118},[29,3304,122],{"class":111},[29,3306,3307],{"class":31,"line":193},[29,3308,129],{"emptyLinePlaceholder":128},[29,3310,3311,3314,3316],{"class":31,"line":202},[29,3312,3313],{"class":111},"    ready.",[29,3315,337],{"class":138},[29,3317,3189],{"class":111},[29,3319,3320,3323],{"class":31,"line":208},[29,3321,3322],{"class":118},"        true",[29,3324,3197],{"class":111},[29,3326,3327,3329],{"class":31,"line":566},[29,3328,1172],{"class":138},[29,3330,3204],{"class":111},[29,3332,3333],{"class":31,"line":593},[29,3334,211],{"class":111},[12,3336,3337],{},"另一个线程即使：",[20,3339,3341],{"className":22,"code":3340,"language":24,"meta":25,"style":25},"ready.load(std::memory_order_relaxed)\n",[15,3342,3343],{"__ignoreMap":25},[29,3344,3345,3347,3349,3351,3353],{"class":31,"line":32},[29,3346,2902],{"class":111},[29,3348,361],{"class":138},[29,3350,340],{"class":111},[29,3352,276],{"class":138},[29,3354,3355],{"class":111},"::memory_order_relaxed)\n",[12,3357,3358,3359,3361,3362,3364],{},"读到了 ",[15,3360,1131],{},"，也不能单纯依靠这个 ",[15,3363,3151],{}," 操作建立：",[20,3366,3369],{"className":3367,"code":3368,"language":73,"meta":25},[71],"data = 42\n\n和\n\n读取 data\n",[15,3370,3368],{"__ignoreMap":25},[12,3372,3373],{},"之间所需要的跨线程同步关系。",[3048,3375],{},[93,3377,3379],{"id":3378},"acquire-release","acquire \u002F release",[12,3381,3382,3384],{},[15,3383,3379],{}," 最经典的用途是：",[48,3386,3387],{},[12,3388,3389],{},[83,3390,3391],{},"一个线程先准备数据，然后通过 atomic 发布“数据已经准备好了”；另一个线程看到这个信号以后再读取数据。",[12,3393,419],{},[20,3395,3397],{"className":22,"code":3396,"language":24,"meta":25,"style":25},"#include \u003Catomic>\n#include \u003Cprint>\n#include \u003Cthread>\n\nint data = 0;\n\nstd::atomic\u003Cbool> ready{false};\n\nvoid producer()\n{\n    data = 42;\n\n    ready.store(\n        true,\n        std::memory_order_release);\n}\n\nvoid consumer()\n{\n    while (!ready.load(\n        std::memory_order_acquire))\n    {\n    }\n\n    std::println(\"{}\", data);\n}\n\nint main()\n{\n    std::thread t1(producer);\n    std::thread t2(consumer);\n\n    t1.join();\n    t2.join();\n}\n",[15,3398,3399,3405,3411,3417,3421,3433,3437,3451,3455,3463,3467,3477,3481,3489,3495,3501,3505,3509,3518,3522,3536,3543,3547,3551,3555,3569,3573,3577,3585,3589,3601,3613,3618,3628,3638],{"__ignoreMap":25},[29,3400,3401,3403],{"class":31,"line":32},[29,3402,36],{"class":35},[29,3404,40],{"class":39},[29,3406,3407,3409],{"class":31,"line":125},[29,3408,36],{"class":35},[29,3410,515],{"class":39},[29,3412,3413,3415],{"class":31,"line":132},[29,3414,36],{"class":35},[29,3416,522],{"class":39},[29,3418,3419],{"class":31,"line":145},[29,3420,129],{"emptyLinePlaceholder":128},[29,3422,3423,3425,3427,3429,3431],{"class":31,"line":151},[29,3424,108],{"class":35},[29,3426,2856],{"class":111},[29,3428,115],{"class":35},[29,3430,119],{"class":118},[29,3432,122],{"class":111},[29,3434,3435],{"class":31,"line":187},[29,3436,129],{"emptyLinePlaceholder":128},[29,3438,3439,3441,3443,3445,3447,3449],{"class":31,"line":193},[29,3440,276],{"class":138},[29,3442,279],{"class":111},[29,3444,1125],{"class":35},[29,3446,2873],{"class":111},[29,3448,1307],{"class":118},[29,3450,291],{"class":111},[29,3452,3453],{"class":31,"line":202},[29,3454,129],{"emptyLinePlaceholder":128},[29,3456,3457,3459,3461],{"class":31,"line":208},[29,3458,135],{"class":35},[29,3460,3287],{"class":138},[29,3462,142],{"class":111},[29,3464,3465],{"class":31,"line":566},[29,3466,148],{"class":111},[29,3468,3469,3471,3473,3475],{"class":31,"line":593},[29,3470,3298],{"class":111},[29,3472,115],{"class":35},[29,3474,2895],{"class":118},[29,3476,122],{"class":111},[29,3478,3479],{"class":31,"line":598},[29,3480,129],{"emptyLinePlaceholder":128},[29,3482,3483,3485,3487],{"class":31,"line":605},[29,3484,3313],{"class":111},[29,3486,337],{"class":138},[29,3488,3189],{"class":111},[29,3490,3491,3493],{"class":31,"line":610},[29,3492,3322],{"class":118},[29,3494,3197],{"class":111},[29,3496,3497,3499],{"class":31,"line":615},[29,3498,1172],{"class":138},[29,3500,2763],{"class":111},[29,3502,3503],{"class":31,"line":620},[29,3504,211],{"class":111},[29,3506,3507],{"class":31,"line":630},[29,3508,129],{"emptyLinePlaceholder":128},[29,3510,3511,3513,3516],{"class":31,"line":635},[29,3512,135],{"class":35},[29,3514,3515],{"class":138}," consumer",[29,3517,142],{"class":111},[29,3519,3520],{"class":31,"line":657},[29,3521,148],{"class":111},[29,3523,3524,3526,3528,3530,3532,3534],{"class":31,"line":662},[29,3525,1155],{"class":35},[29,3527,157],{"class":111},[29,3529,1859],{"class":35},[29,3531,2902],{"class":111},[29,3533,361],{"class":138},[29,3535,3189],{"class":111},[29,3537,3538,3540],{"class":31,"line":690},[29,3539,1172],{"class":138},[29,3541,3542],{"class":111},"::memory_order_acquire))\n",[29,3544,3545],{"class":31,"line":695},[29,3546,190],{"class":111},[29,3548,3549],{"class":31,"line":707},[29,3550,205],{"class":111},[29,3552,3553],{"class":31,"line":712},[29,3554,129],{"emptyLinePlaceholder":128},[29,3556,3557,3559,3561,3563,3565,3567],{"class":31,"line":717},[29,3558,638],{"class":138},[29,3560,761],{"class":111},[29,3562,764],{"class":138},[29,3564,340],{"class":111},[29,3566,769],{"class":39},[29,3568,2961],{"class":111},[29,3570,3571],{"class":31,"line":730},[29,3572,211],{"class":111},[29,3574,3575],{"class":31,"line":735},[29,3576,129],{"emptyLinePlaceholder":128},[29,3578,3579,3581,3583],{"class":31,"line":746},[29,3580,108],{"class":35},[29,3582,625],{"class":138},[29,3584,142],{"class":111},[29,3586,3587],{"class":31,"line":751},[29,3588,148],{"class":111},[29,3590,3591,3593,3595,3598],{"class":31,"line":756},[29,3592,638],{"class":138},[29,3594,1254],{"class":111},[29,3596,3597],{"class":138},"t1",[29,3599,3600],{"class":111},"(producer);\n",[29,3602,3603,3605,3607,3610],{"class":31,"line":780},[29,3604,638],{"class":138},[29,3606,1254],{"class":111},[29,3608,3609],{"class":138},"t2",[29,3611,3612],{"class":111},"(consumer);\n",[29,3614,3616],{"class":31,"line":3615},32,[29,3617,129],{"emptyLinePlaceholder":128},[29,3619,3621,3624,3626],{"class":31,"line":3620},33,[29,3622,3623],{"class":111},"    t1.",[29,3625,741],{"class":138},[29,3627,364],{"class":111},[29,3629,3631,3634,3636],{"class":31,"line":3630},34,[29,3632,3633],{"class":111},"    t2.",[29,3635,741],{"class":138},[29,3637,364],{"class":111},[29,3639,3641],{"class":31,"line":3640},35,[29,3642,211],{"class":111},[12,3644,787],{},[20,3646,3649],{"className":3647,"code":3648,"language":73,"meta":25},[71],"42\n",[15,3650,3648],{"__ignoreMap":25},[12,3652,3653],{},"可以这样理解：",[20,3655,3658],{"className":3656,"code":3657,"language":73,"meta":25},[71],"producer：\n\ndata = 42\n↓\n先把普通数据准备好\n↓\nrelease store\n↓\n发布 ready = true\n",[15,3659,3657],{"__ignoreMap":25},[12,3661,3662],{},"consumer：",[20,3664,3667],{"className":3665,"code":3666,"language":73,"meta":25},[71],"acquire load\n↓\n看到 ready == true\n↓\n接收到 producer 发布的数据\n↓\n读取 data\n",[15,3668,3666],{"__ignoreMap":25},[12,3670,3671],{},"也就是：",[20,3673,3676],{"className":3674,"code":3675,"language":73,"meta":25},[71],"写普通数据\n\n      ↓\n\nrelease\n\n      ↓\n\nacquire\n\n      ↓\n\n读普通数据\n",[15,3677,3675],{"__ignoreMap":25},[12,3679,1787,3680,3683],{},[15,3681,3682],{},"ready"," 本身只是一个同步信号。",[12,3685,3686],{},"真正重要的是：",[48,3688,3689],{},[12,3690,3691,3692,3695,3696,3699],{},"producer 在 ",[15,3693,3694],{},"release"," 之前完成的操作，可以通过对应的 ",[15,3697,3698],{},"acquire"," 与 consumer 建立同步关系。",[12,3701,3702],{},"所以可以简单记成：",[20,3704,3707],{"className":3705,"code":3706,"language":73,"meta":25},[71],"release\n→ 发布之前完成的工作\n\nacquire\n→ 接收对方发布的工作\n",[15,3708,3706],{"__ignoreMap":25},[3048,3710],{},[93,3712,3714],{"id":3713},"memory_order_acq_rel",[15,3715,3713],{},[12,3717,3718],{},"有些原子操作既会读取旧值，又会写入新值，例如：",[20,3720,3722],{"className":22,"code":3721,"language":24,"meta":25,"style":25},"fetch_add()\nexchange()\ncompare_exchange()\n",[15,3723,3724,3730,3736],{"__ignoreMap":25},[29,3725,3726,3728],{"class":31,"line":32},[29,3727,809],{"class":138},[29,3729,142],{"class":111},[29,3731,3732,3734],{"class":31,"line":125},[29,3733,943],{"class":138},[29,3735,142],{"class":111},[29,3737,3738,3740],{"class":31,"line":132},[29,3739,1527],{"class":138},[29,3741,142],{"class":111},[12,3743,3744],{},"这种操作有时同时需要：",[20,3746,3749],{"className":3747,"code":3748,"language":73,"meta":25},[71],"acquire\n\n+\n\nrelease\n",[15,3750,3748],{"__ignoreMap":25},[12,3752,3753],{},"于是可以使用：",[20,3755,3757],{"className":22,"code":3756,"language":24,"meta":25,"style":25},"std::memory_order_acq_rel\n",[15,3758,3759],{"__ignoreMap":25},[29,3760,3761,3763],{"class":31,"line":32},[29,3762,276],{"class":138},[29,3764,3018],{"class":111},[12,3766,3767],{},"可以先简单理解成：",[20,3769,3772],{"className":3770,"code":3771,"language":73,"meta":25},[71],"既接收别人之前发布的数据\n\n又把自己之前完成的操作继续发布出去\n",[15,3773,3771],{"__ignoreMap":25},[12,3775,3776],{},"这一部分更多出现在复杂的无锁数据结构中，初学阶段知道它的含义即可。",[3048,3778],{},[12,3780,3781],{},"最重要的是记住：",[20,3783,3786],{"className":3784,"code":3785,"language":73,"meta":25},[71],"原子性\n→ atomic 自己的操作是否不可分割\n\n内存序\n→ atomic 操作和其他内存访问之间建立怎样的跨线程顺序\n",[15,3787,3785],{"__ignoreMap":25},[12,3789,3790],{},"以及一个实用原则：",[48,3792,3793],{},[12,3794,3795],{},[83,3796,3797,3798,3800,3801,2715,3803,2715,3805,3807],{},"默认先用 ",[15,3799,3056],{},"；只有在明确理解同步关系，并且确实有必要时，再主动使用 ",[15,3802,3151],{},[15,3804,3698],{},[15,3806,3694],{}," 等更弱的内存序。",[88,3809,3810],{"id":3810},"等待与实现特性",[93,3812,3814],{"id":3813},"is_lock_free",[15,3815,3816],{},"is_lock_free()",[12,3818,3819,3821],{},[15,3820,17],{}," 保证的是：",[48,3823,3824],{},[12,3825,3826],{},"对这个对象的原子操作具有标准规定的线程安全语义。",[12,3828,3829,3830,479],{},"但它",[83,3831,3832],{},"不保证底层一定完全不用锁",[12,3834,419],{},[20,3836,3838],{"className":22,"code":3837,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{0};\n\nbool lock_free = value.is_lock_free();\n",[15,3839,3840,3854,3858],{"__ignoreMap":25},[29,3841,3842,3844,3846,3848,3850,3852],{"class":31,"line":32},[29,3843,276],{"class":138},[29,3845,279],{"class":111},[29,3847,282],{"class":35},[29,3849,321],{"class":111},[29,3851,288],{"class":118},[29,3853,291],{"class":111},[29,3855,3856],{"class":31,"line":125},[29,3857,129],{"emptyLinePlaceholder":128},[29,3859,3860,3862,3865,3867,3869,3871],{"class":31,"line":132},[29,3861,1761],{"class":35},[29,3863,3864],{"class":111}," lock_free ",[29,3866,115],{"class":35},[29,3868,358],{"class":111},[29,3870,3813],{"class":138},[29,3872,364],{"class":111},[12,3874,3875,3877],{},[15,3876,3816],{}," 用来查询：",[48,3879,3880],{},[12,3881,3882],{},"当前这个原子对象，在当前平台和当前实现中，底层操作是否可以不依赖额外的锁来完成。",[12,3884,3885],{},"如果返回：",[20,3887,3890],{"className":3888,"code":3889,"language":73,"meta":25},[71],"true\n",[15,3891,3889],{"__ignoreMap":25},[12,3893,3894],{},"说明它的原子操作是 lock-free 的，也就是底层通常可以直接依靠 CPU 提供的原子指令完成。",[12,3896,3885],{},[20,3898,3901],{"className":3899,"code":3900,"language":73,"meta":25},[71],"false\n",[15,3902,3900],{"__ignoreMap":25},[12,3904,3905],{},"说明标准库为了实现这个原子操作，内部可能需要借助某种锁或其他同步机制。",[12,3907,419],{},[20,3909,3911],{"className":22,"code":3910,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{0};\n\nstd::println(\"{}\", value.is_lock_free());\n",[15,3912,3913,3927,3931],{"__ignoreMap":25},[29,3914,3915,3917,3919,3921,3923,3925],{"class":31,"line":32},[29,3916,276],{"class":138},[29,3918,279],{"class":111},[29,3920,282],{"class":35},[29,3922,321],{"class":111},[29,3924,288],{"class":118},[29,3926,291],{"class":111},[29,3928,3929],{"class":31,"line":125},[29,3930,129],{"emptyLinePlaceholder":128},[29,3932,3933,3935,3937,3939,3941,3943,3945,3947],{"class":31,"line":132},[29,3934,276],{"class":138},[29,3936,761],{"class":111},[29,3938,764],{"class":138},[29,3940,340],{"class":111},[29,3942,769],{"class":39},[29,3944,1696],{"class":111},[29,3946,3813],{"class":138},[29,3948,777],{"class":111},[12,3950,3951],{},"在常见的 x86-64、ARM 等平台上，像：",[20,3953,3955],{"className":22,"code":3954,"language":24,"meta":25,"style":25},"std::atomic\u003Cint>\nstd::atomic\u003Cbool>\nstd::atomic\u003Cint*>\n",[15,3956,3957,3966,3975],{"__ignoreMap":25},[29,3958,3959,3961,3963],{"class":31,"line":32},[29,3960,276],{"class":138},[29,3962,279],{"class":111},[29,3964,3965],{"class":35},"\u003Cint>\n",[29,3967,3968,3970,3972],{"class":31,"line":125},[29,3969,276],{"class":138},[29,3971,279],{"class":111},[29,3973,3974],{"class":35},"\u003Cbool>\n",[29,3976,3977,3979,3981],{"class":31,"line":132},[29,3978,276],{"class":138},[29,3980,279],{"class":111},[29,3982,3983],{"class":35},"\u003Cint*>\n",[12,3985,3986],{},"这种比较小的类型通常都是 lock-free 的。",[12,3988,3989],{},"但是对于比较大的类型，情况就不一定了。",[12,3991,419],{},[20,3993,3995],{"className":22,"code":3994,"language":24,"meta":25,"style":25},"struct Data\n{\n    long long a;\n    long long b;\n};\n\nstd::atomic\u003CData> data;\n",[15,3996,3997,4005,4009,4020,4029,4033,4037],{"__ignoreMap":25},[29,3998,3999,4002],{"class":31,"line":32},[29,4000,4001],{"class":35},"struct",[29,4003,4004],{"class":138}," Data\n",[29,4006,4007],{"class":31,"line":125},[29,4008,148],{"class":111},[29,4010,4011,4014,4017],{"class":31,"line":132},[29,4012,4013],{"class":35},"    long",[29,4015,4016],{"class":35}," long",[29,4018,4019],{"class":111}," a;\n",[29,4021,4022,4024,4026],{"class":31,"line":145},[29,4023,4013],{"class":35},[29,4025,4016],{"class":35},[29,4027,4028],{"class":111}," b;\n",[29,4030,4031],{"class":31,"line":151},[29,4032,291],{"class":111},[29,4034,4035],{"class":31,"line":187},[29,4036,129],{"emptyLinePlaceholder":128},[29,4038,4039,4041,4043,4045,4048,4050],{"class":31,"line":193},[29,4040,276],{"class":138},[29,4042,279],{"class":111},[29,4044,172],{"class":35},[29,4046,4047],{"class":111},"Data",[29,4049,651],{"class":35},[29,4051,4052],{"class":111}," data;\n",[12,4054,4055],{},"这种对象比较大，CPU 可能没有办法用一条或少量原子指令直接完成操作。",[12,4057,4058],{},"此时标准库仍然可以提供：",[20,4060,4062],{"className":22,"code":4061,"language":24,"meta":25,"style":25},"data.load();\ndata.store(...);\n",[15,4063,4064,4073],{"__ignoreMap":25},[29,4065,4066,4069,4071],{"class":31,"line":32},[29,4067,4068],{"class":111},"data.",[29,4070,361],{"class":138},[29,4072,364],{"class":111},[29,4074,4075,4077,4079],{"class":31,"line":125},[29,4076,4068],{"class":111},[29,4078,337],{"class":138},[29,4080,4081],{"class":111},"(...);\n",[12,4083,4084],{},"这样的原子语义，但内部可能需要通过锁来实现。",[12,4086,4087],{},"因此：",[20,4089,4091],{"className":22,"code":4090,"language":24,"meta":25,"style":25},"std::atomic\u003CT>\n",[15,4092,4093],{"__ignoreMap":25},[29,4094,4095,4097,4099,4101,4104],{"class":31,"line":32},[29,4096,276],{"class":138},[29,4098,279],{"class":111},[29,4100,172],{"class":35},[29,4102,4103],{"class":111},"T",[29,4105,4106],{"class":35},">\n",[12,4108,4109],{},"应该理解成：",[48,4111,4112],{},[12,4113,4114],{},"保证“操作是原子的”。",[12,4116,4117],{},"而不是：",[48,4119,4120],{},[12,4121,4122],{},"保证“实现一定无锁”。",[3048,4124],{},[12,4126,4127],{},"还可以使用：",[20,4129,4131],{"className":22,"code":4130,"language":24,"meta":25,"style":25},"std::atomic\u003Cint>::is_always_lock_free\n",[15,4132,4133],{"__ignoreMap":25},[29,4134,4135,4137,4139,4141,4143,4145],{"class":31,"line":32},[29,4136,276],{"class":138},[29,4138,761],{"class":111},[29,4140,63],{"class":138},[29,4142,172],{"class":111},[29,4144,108],{"class":35},[29,4146,4147],{"class":111},">::is_always_lock_free\n",[12,4149,4150],{},"它是一个编译期常量，用来判断：",[48,4152,4153],{},[12,4154,4155,4156,4159],{},"这个 ",[15,4157,4158],{},"std::atomic\u003Cint>"," 类型在当前实现中是否永远都是 lock-free 的。",[12,4161,419],{},[20,4163,4165],{"className":22,"code":4164,"language":24,"meta":25,"style":25},"if constexpr (std::atomic\u003Cint>::is_always_lock_free)\n{\n    \u002F\u002F 当前实现保证 atomic\u003Cint> 永远是 lock-free\n}\n",[15,4166,4167,4190,4194,4199],{"__ignoreMap":25},[29,4168,4169,4172,4175,4177,4179,4181,4183,4185,4187],{"class":31,"line":32},[29,4170,4171],{"class":35},"if",[29,4173,4174],{"class":35}," constexpr",[29,4176,157],{"class":111},[29,4178,276],{"class":138},[29,4180,761],{"class":111},[29,4182,63],{"class":138},[29,4184,172],{"class":111},[29,4186,108],{"class":35},[29,4188,4189],{"class":111},">::is_always_lock_free)\n",[29,4191,4192],{"class":31,"line":125},[29,4193,148],{"class":111},[29,4195,4196],{"class":31,"line":132},[29,4197,4198],{"class":2143},"    \u002F\u002F 当前实现保证 atomic\u003Cint> 永远是 lock-free\n",[29,4200,4201],{"class":31,"line":145},[29,4202,211],{"class":111},[12,4204,4205],{},"可以简单区分成：",[20,4207,4210],{"className":4208,"code":4209,"language":73,"meta":25},[71],"value.is_lock_free()\n",[15,4211,4209],{"__ignoreMap":25},[12,4213,4214],{},"表示：",[48,4216,4217],{},[12,4218,4219],{},"这个具体 atomic 对象现在是不是 lock-free。",[12,4221,4222],{},"而：",[20,4224,4227],{"className":4225,"code":4226,"language":73,"meta":25},[71],"std::atomic\u003CT>::is_always_lock_free\n",[15,4228,4226],{"__ignoreMap":25},[12,4230,4214],{},[48,4232,4233],{},[12,4234,4235],{},"这个 atomic 类型在当前实现中是不是始终保证 lock-free。",[12,4237,4238],{},"所以最重要的一点是：",[20,4240,4243],{"className":4241,"code":4242,"language":73,"meta":25},[71],"atomic ≠ 一定无锁\n",[15,4244,4242],{"__ignoreMap":25},[12,4246,4247,4249],{},[15,4248,63],{}," 真正表示的是：",[20,4251,4254],{"className":4252,"code":4253,"language":73,"meta":25},[71],"原子操作语义\n",[15,4255,4253],{"__ignoreMap":25},[12,4257,4258],{},"也就是多个线程同时访问时，这些操作不会被拆成一半让其他线程插进来。",[12,4260,4261],{},"至于底层到底是：",[20,4263,4266],{"className":4264,"code":4265,"language":73,"meta":25},[71],"CPU 原子指令\n",[15,4267,4265],{"__ignoreMap":25},[12,4269,4270],{},"还是：",[20,4272,4275],{"className":4273,"code":4274,"language":73,"meta":25},[71],"标准库内部借助锁实现\n",[15,4276,4274],{"__ignoreMap":25},[12,4278,4279],{},"这是具体平台和标准库实现决定的。",[12,4281,4282],{},"因此可以记成一句话：",[48,4284,4285],{},[12,4286,4287,4289,4290,4292],{},[15,4288,17],{}," 保证的是“原子性”，不保证“一定无锁”；",[15,4291,3816],{}," 就是用来查询它底层是否真正采用无锁实现的。",[93,4294,4296,4297,401,4300,401,4303],{"id":4295},"c20wait-notify_one-notify_all","C++20：",[15,4298,4299],{},"wait()",[15,4301,4302],{},"notify_one()",[15,4304,4305],{},"notify_all()",[12,4307,4308],{},"C++20 给原子类型增加了：",[20,4310,4312],{"className":22,"code":4311,"language":24,"meta":25,"style":25},"wait()\nnotify_one()\nnotify_all()\n",[15,4313,4314,4321,4328],{"__ignoreMap":25},[29,4315,4316,4319],{"class":31,"line":32},[29,4317,4318],{"class":138},"wait",[29,4320,142],{"class":111},[29,4322,4323,4326],{"class":31,"line":125},[29,4324,4325],{"class":138},"notify_one",[29,4327,142],{"class":111},[29,4329,4330,4333],{"class":31,"line":132},[29,4331,4332],{"class":138},"notify_all",[29,4334,142],{"class":111},[12,4336,4337],{},"它们可以让线程等待某个原子变量的值发生变化，而不需要自己一直写循环忙等。",[12,4339,419],{},[20,4341,4343],{"className":22,"code":4342,"language":24,"meta":25,"style":25},"#include \u003Catomic>\n#include \u003Cprint>\n#include \u003Cthread>\n\nstd::atomic\u003Cbool> ready{false};\n\nvoid worker()\n{\n    ready.wait(false);\n\n    std::println(\"start\");\n}\n\nint main()\n{\n    std::thread t(worker);\n\n    ready.store(true);\n    ready.notify_one();\n\n    t.join();\n}\n",[15,4344,4345,4351,4357,4363,4367,4381,4385,4393,4397,4409,4413,4428,4432,4436,4444,4448,4458,4462,4474,4482,4486,4494],{"__ignoreMap":25},[29,4346,4347,4349],{"class":31,"line":32},[29,4348,36],{"class":35},[29,4350,40],{"class":39},[29,4352,4353,4355],{"class":31,"line":125},[29,4354,36],{"class":35},[29,4356,515],{"class":39},[29,4358,4359,4361],{"class":31,"line":132},[29,4360,36],{"class":35},[29,4362,522],{"class":39},[29,4364,4365],{"class":31,"line":145},[29,4366,129],{"emptyLinePlaceholder":128},[29,4368,4369,4371,4373,4375,4377,4379],{"class":31,"line":151},[29,4370,276],{"class":138},[29,4372,279],{"class":111},[29,4374,1125],{"class":35},[29,4376,2873],{"class":111},[29,4378,1307],{"class":118},[29,4380,291],{"class":111},[29,4382,4383],{"class":31,"line":187},[29,4384,129],{"emptyLinePlaceholder":128},[29,4386,4387,4389,4391],{"class":31,"line":193},[29,4388,135],{"class":35},[29,4390,1144],{"class":138},[29,4392,142],{"class":111},[29,4394,4395],{"class":31,"line":202},[29,4396,148],{"class":111},[29,4398,4399,4401,4403,4405,4407],{"class":31,"line":208},[29,4400,3313],{"class":111},[29,4402,4318],{"class":138},[29,4404,340],{"class":111},[29,4406,1307],{"class":118},[29,4408,346],{"class":111},[29,4410,4411],{"class":31,"line":566},[29,4412,129],{"emptyLinePlaceholder":128},[29,4414,4415,4417,4419,4421,4423,4426],{"class":31,"line":593},[29,4416,638],{"class":138},[29,4418,761],{"class":111},[29,4420,764],{"class":138},[29,4422,340],{"class":111},[29,4424,4425],{"class":39},"\"start\"",[29,4427,346],{"class":111},[29,4429,4430],{"class":31,"line":598},[29,4431,211],{"class":111},[29,4433,4434],{"class":31,"line":605},[29,4435,129],{"emptyLinePlaceholder":128},[29,4437,4438,4440,4442],{"class":31,"line":610},[29,4439,108],{"class":35},[29,4441,625],{"class":138},[29,4443,142],{"class":111},[29,4445,4446],{"class":31,"line":615},[29,4447,148],{"class":111},[29,4449,4450,4452,4454,4456],{"class":31,"line":620},[29,4451,638],{"class":138},[29,4453,1254],{"class":111},[29,4455,1257],{"class":138},[29,4457,1260],{"class":111},[29,4459,4460],{"class":31,"line":630},[29,4461,129],{"emptyLinePlaceholder":128},[29,4463,4464,4466,4468,4470,4472],{"class":31,"line":635},[29,4465,3313],{"class":111},[29,4467,337],{"class":138},[29,4469,340],{"class":111},[29,4471,1131],{"class":118},[29,4473,346],{"class":111},[29,4475,4476,4478,4480],{"class":31,"line":657},[29,4477,3313],{"class":111},[29,4479,4325],{"class":138},[29,4481,364],{"class":111},[29,4483,4484],{"class":31,"line":662},[29,4485,129],{"emptyLinePlaceholder":128},[29,4487,4488,4490,4492],{"class":31,"line":690},[29,4489,1318],{"class":111},[29,4491,741],{"class":138},[29,4493,364],{"class":111},[29,4495,4496],{"class":31,"line":695},[29,4497,211],{"class":111},[12,4499,787],{},[20,4501,4504],{"className":4502,"code":4503,"language":73,"meta":25},[71],"start\n",[15,4505,4503],{"__ignoreMap":25},[12,4507,4508],{},"这里最关键的是：",[20,4510,4512],{"className":22,"code":4511,"language":24,"meta":25,"style":25},"ready.wait(false);\n",[15,4513,4514],{"__ignoreMap":25},[29,4515,4516,4518,4520,4522,4524],{"class":31,"line":32},[29,4517,2902],{"class":111},[29,4519,4318],{"class":138},[29,4521,340],{"class":111},[29,4523,1307],{"class":118},[29,4525,346],{"class":111},[12,4527,4528,4529,1994],{},"它的意思",[83,4530,4531],{},"不是",[20,4533,4536],{"className":4534,"code":4535,"language":73,"meta":25},[71],"等待 ready 变成 false\n",[15,4537,4535],{"__ignoreMap":25},[12,4539,4540],{},"而是：",[48,4542,4543],{},[12,4544,930,4545,4547,4548,4550,4551,4553],{},[15,4546,3682],{}," 当前仍然等于 ",[15,4549,1307],{},"，就等待；直到它不再等于 ",[15,4552,1307],{}," 才返回。",[12,4555,4556],{},"所以执行过程可以理解成：",[20,4558,4561],{"className":4559,"code":4560,"language":73,"meta":25},[71],"初始：\n\nready = false\n",[15,4562,4560],{"__ignoreMap":25},[12,4564,4565,4568],{},[15,4566,4567],{},"worker"," 执行：",[20,4570,4571],{"className":22,"code":4511,"language":24,"meta":25,"style":25},[15,4572,4573],{"__ignoreMap":25},[29,4574,4575,4577,4579,4581,4583],{"class":31,"line":32},[29,4576,2902],{"class":111},[29,4578,4318],{"class":138},[29,4580,340],{"class":111},[29,4582,1307],{"class":118},[29,4584,346],{"class":111},[12,4586,4587],{},"发现：",[20,4589,4592],{"className":4590,"code":4591,"language":73,"meta":25},[71],"ready == false\n",[15,4593,4591],{"__ignoreMap":25},[12,4595,4596,4597,4599],{},"也就是当前值和传进去的旧值 ",[15,4598,1307],{}," 相同，于是开始等待。",[12,4601,4602,4603,4568],{},"随后 ",[15,4604,4605],{},"main",[20,4607,4609],{"className":22,"code":4608,"language":24,"meta":25,"style":25},"ready.store(true);\n",[15,4610,4611],{"__ignoreMap":25},[29,4612,4613,4615,4617,4619,4621],{"class":31,"line":32},[29,4614,2902],{"class":111},[29,4616,337],{"class":138},[29,4618,340],{"class":111},[29,4620,1131],{"class":118},[29,4622,346],{"class":111},[12,4624,2284],{},[20,4626,4629],{"className":4627,"code":4628,"language":73,"meta":25},[71],"ready = false\n",[15,4630,4628],{"__ignoreMap":25},[12,4632,4633],{},"改成：",[20,4635,4638],{"className":4636,"code":4637,"language":73,"meta":25},[71],"ready = true\n",[15,4639,4637],{"__ignoreMap":25},[12,4641,4642],{},"然后：",[20,4644,4646],{"className":22,"code":4645,"language":24,"meta":25,"style":25},"ready.notify_one();\n",[15,4647,4648],{"__ignoreMap":25},[29,4649,4650,4652,4654],{"class":31,"line":32},[29,4651,2902],{"class":111},[29,4653,4325],{"class":138},[29,4655,364],{"class":111},[12,4657,4658,4659,4661],{},"通知一个正在等待 ",[15,4660,3682],{}," 的线程：",[48,4663,4664],{},[12,4665,4666],{},"这个值可能发生变化了，你可以重新检查一下。",[12,4668,4669,4671],{},[15,4670,4567],{}," 被唤醒后发现：",[20,4673,4676],{"className":4674,"code":4675,"language":73,"meta":25},[71],"ready != false\n",[15,4677,4675],{"__ignoreMap":25},[12,4679,4680],{},"于是：",[20,4682,4683],{"className":22,"code":4511,"language":24,"meta":25,"style":25},[15,4684,4685],{"__ignoreMap":25},[29,4686,4687,4689,4691,4693,4695],{"class":31,"line":32},[29,4688,2902],{"class":111},[29,4690,4318],{"class":138},[29,4692,340],{"class":111},[29,4694,1307],{"class":118},[29,4696,346],{"class":111},[12,4698,4699],{},"返回，继续向下执行：",[20,4701,4703],{"className":22,"code":4702,"language":24,"meta":25,"style":25},"std::println(\"start\");\n",[15,4704,4705],{"__ignoreMap":25},[29,4706,4707,4709,4711,4713,4715,4717],{"class":31,"line":32},[29,4708,276],{"class":138},[29,4710,761],{"class":111},[29,4712,764],{"class":138},[29,4714,340],{"class":111},[29,4716,4425],{"class":39},[29,4718,346],{"class":111},[12,4720,4721],{},"所以整个过程可以画成：",[20,4723,4726],{"className":4724,"code":4725,"language":73,"meta":25},[71],"worker                         main\n   |                            |\n   | ready.wait(false)          |\n   |                            |\n   | ready == false             |\n   | 开始等待                   |\n   |                            |\n   |                    ready.store(true)\n   |                            |\n   |                    ready.notify_one()\n   |                            |\n   | \u003C------ 被唤醒 ------------|\n   |\n   | ready != false\n   |\n   | wait() 返回\n   |\n   | println(\"start\")\n",[15,4727,4725],{"__ignoreMap":25},[3048,4729],{},[12,4731,4732,4733,4735],{},"如果不用 ",[15,4734,4299],{},"，以前可能会写成：",[20,4737,4739],{"className":22,"code":4738,"language":24,"meta":25,"style":25},"while (!ready.load())\n{\n}\n",[15,4740,4741,4755,4759],{"__ignoreMap":25},[29,4742,4743,4745,4747,4749,4751,4753],{"class":31,"line":32},[29,4744,1854],{"class":35},[29,4746,157],{"class":111},[29,4748,1859],{"class":35},[29,4750,2902],{"class":111},[29,4752,361],{"class":138},[29,4754,1163],{"class":111},[29,4756,4757],{"class":31,"line":125},[29,4758,148],{"class":111},[29,4760,4761],{"class":31,"line":132},[29,4762,211],{"class":111},[12,4764,4765],{},"这个循环会不断执行：",[20,4767,4769],{"className":22,"code":4768,"language":24,"meta":25,"style":25},"ready.load();\nready.load();\nready.load();\nready.load();\n...\n",[15,4770,4771,4779,4787,4795,4803],{"__ignoreMap":25},[29,4772,4773,4775,4777],{"class":31,"line":32},[29,4774,2902],{"class":111},[29,4776,361],{"class":138},[29,4778,364],{"class":111},[29,4780,4781,4783,4785],{"class":31,"line":125},[29,4782,2902],{"class":111},[29,4784,361],{"class":138},[29,4786,364],{"class":111},[29,4788,4789,4791,4793],{"class":31,"line":132},[29,4790,2902],{"class":111},[29,4792,361],{"class":138},[29,4794,364],{"class":111},[29,4796,4797,4799,4801],{"class":31,"line":145},[29,4798,2902],{"class":111},[29,4800,361],{"class":138},[29,4802,364],{"class":111},[29,4804,4805],{"class":31,"line":151},[29,4806,2265],{"class":111},[12,4808,4809],{},"也就是线程一直占着 CPU 检查：",[48,4811,4812],{},[12,4813,4814,4815,4817],{},"变成 ",[15,4816,1131],{}," 了吗？",[12,4819,4820,4821,479],{},"这种方式叫做",[83,4822,4823],{},"忙等 \u002F 自旋等待",[12,4825,4222],{},[20,4827,4828],{"className":22,"code":4511,"language":24,"meta":25,"style":25},[15,4829,4830],{"__ignoreMap":25},[29,4831,4832,4834,4836,4838,4840],{"class":31,"line":32},[29,4833,2902],{"class":111},[29,4835,4318],{"class":138},[29,4837,340],{"class":111},[29,4839,1307],{"class":118},[29,4841,346],{"class":111},[12,4843,4844],{},"在需要等待较长时间时，可以让线程阻塞等待，不需要一直空转消耗 CPU。",[12,4846,4087],{},[20,4848,4849],{"className":22,"code":4738,"language":24,"meta":25,"style":25},[15,4850,4851,4865,4869],{"__ignoreMap":25},[29,4852,4853,4855,4857,4859,4861,4863],{"class":31,"line":32},[29,4854,1854],{"class":35},[29,4856,157],{"class":111},[29,4858,1859],{"class":35},[29,4860,2902],{"class":111},[29,4862,361],{"class":138},[29,4864,1163],{"class":111},[29,4866,4867],{"class":31,"line":125},[29,4868,148],{"class":111},[29,4870,4871],{"class":31,"line":132},[29,4872,211],{"class":111},[12,4874,4875],{},"可以理解成：",[48,4877,4878],{},[12,4879,4880],{},"我一直盯着门看它有没有开。",[12,4882,4222],{},[20,4884,4885],{"className":22,"code":4511,"language":24,"meta":25,"style":25},[15,4886,4887],{"__ignoreMap":25},[29,4888,4889,4891,4893,4895,4897],{"class":31,"line":32},[29,4890,2902],{"class":111},[29,4892,4318],{"class":138},[29,4894,340],{"class":111},[29,4896,1307],{"class":118},[29,4898,346],{"class":111},[12,4900,4901],{},"更像：",[48,4903,4904],{},[12,4905,4906],{},"门没开我就先睡觉，等有人叫我再起来看看。",[3048,4908],{},[93,4910,4912,299,4914],{"id":4911},"notify_one-和-notify_all",[15,4913,4302],{},[15,4915,4305],{},[12,4917,4918],{},"如果只有一个等待线程，可以：",[20,4920,4921],{"className":22,"code":4645,"language":24,"meta":25,"style":25},[15,4922,4923],{"__ignoreMap":25},[29,4924,4925,4927,4929],{"class":31,"line":32},[29,4926,2902],{"class":111},[29,4928,4325],{"class":138},[29,4930,364],{"class":111},[12,4932,4214],{},[48,4934,4935],{},[12,4936,4937],{},"唤醒一个等待这个原子变量的线程。",[12,4939,4940],{},"如果有多个线程：",[20,4942,4944],{"className":22,"code":4943,"language":24,"meta":25,"style":25},"void worker()\n{\n    ready.wait(false);\n    std::println(\"start\");\n}\n",[15,4945,4946,4954,4958,4970,4984],{"__ignoreMap":25},[29,4947,4948,4950,4952],{"class":31,"line":32},[29,4949,135],{"class":35},[29,4951,1144],{"class":138},[29,4953,142],{"class":111},[29,4955,4956],{"class":31,"line":125},[29,4957,148],{"class":111},[29,4959,4960,4962,4964,4966,4968],{"class":31,"line":132},[29,4961,3313],{"class":111},[29,4963,4318],{"class":138},[29,4965,340],{"class":111},[29,4967,1307],{"class":118},[29,4969,346],{"class":111},[29,4971,4972,4974,4976,4978,4980,4982],{"class":31,"line":145},[29,4973,638],{"class":138},[29,4975,761],{"class":111},[29,4977,764],{"class":138},[29,4979,340],{"class":111},[29,4981,4425],{"class":39},[29,4983,346],{"class":111},[29,4985,4986],{"class":31,"line":151},[29,4987,211],{"class":111},[12,4989,4990],{},"然后创建：",[20,4992,4994],{"className":22,"code":4993,"language":24,"meta":25,"style":25},"std::thread t1(worker);\nstd::thread t2(worker);\nstd::thread t3(worker);\n",[15,4995,4996,5014,5031],{"__ignoreMap":25},[29,4997,4998,5000,5002,5005,5008,5010,5012],{"class":31,"line":32},[29,4999,276],{"class":138},[29,5001,761],{"class":111},[29,5003,5004],{"class":138},"thread",[29,5006,5007],{"class":138}," t1",[29,5009,340],{"class":111},[29,5011,4567],{"class":138},[29,5013,346],{"class":111},[29,5015,5016,5018,5020,5022,5025,5027,5029],{"class":31,"line":125},[29,5017,276],{"class":138},[29,5019,761],{"class":111},[29,5021,5004],{"class":138},[29,5023,5024],{"class":138}," t2",[29,5026,340],{"class":111},[29,5028,4567],{"class":138},[29,5030,346],{"class":111},[29,5032,5033,5035,5037,5039,5042,5044,5046],{"class":31,"line":132},[29,5034,276],{"class":138},[29,5036,761],{"class":111},[29,5038,5004],{"class":138},[29,5040,5041],{"class":138}," t3",[29,5043,340],{"class":111},[29,5045,4567],{"class":138},[29,5047,346],{"class":111},[12,5049,5050],{},"如果希望全部线程都被唤醒，可以：",[20,5052,5054],{"className":22,"code":5053,"language":24,"meta":25,"style":25},"ready.store(true);\nready.notify_all();\n",[15,5055,5056,5068],{"__ignoreMap":25},[29,5057,5058,5060,5062,5064,5066],{"class":31,"line":32},[29,5059,2902],{"class":111},[29,5061,337],{"class":138},[29,5063,340],{"class":111},[29,5065,1131],{"class":118},[29,5067,346],{"class":111},[29,5069,5070,5072,5074],{"class":31,"line":125},[29,5071,2902],{"class":111},[29,5073,4332],{"class":138},[29,5075,364],{"class":111},[12,5077,3671],{},[20,5079,5082],{"className":5080,"code":5081,"language":73,"meta":25},[71],"notify_one()  → 通知一个等待线程\n\nnotify_all()  → 通知所有等待线程\n",[15,5083,5081],{"__ignoreMap":25},[3048,5085],{},[93,5087,5089,5091],{"id":5088},"wait-到底在等什么",[15,5090,4299],{}," 到底在等什么？",[12,5093,5094],{},"记住这个规则就够了：",[20,5096,5098],{"className":22,"code":5097,"language":24,"meta":25,"style":25},"atomic.wait(old_value);\n",[15,5099,5100],{"__ignoreMap":25},[29,5101,5102,5105,5107],{"class":31,"line":32},[29,5103,5104],{"class":111},"atomic.",[29,5106,4318],{"class":138},[29,5108,5109],{"class":111},"(old_value);\n",[12,5111,5112],{},"意思是：",[48,5114,5115],{},[12,5116,5117,5118,5121,5122,5124],{},"只要当前值仍然等于 ",[15,5119,5120],{},"old_value","，就继续等待；一旦当前值不等于 ",[15,5123,5120],{},"，就返回。",[12,5126,419],{},[20,5128,5130],{"className":22,"code":5129,"language":24,"meta":25,"style":25},"std::atomic\u003Cint> value{10};\n\nvalue.wait(10);\n",[15,5131,5132,5146,5150],{"__ignoreMap":25},[29,5133,5134,5136,5138,5140,5142,5144],{"class":31,"line":32},[29,5135,276],{"class":138},[29,5137,279],{"class":111},[29,5139,282],{"class":35},[29,5141,321],{"class":111},[29,5143,343],{"class":118},[29,5145,291],{"class":111},[29,5147,5148],{"class":31,"line":125},[29,5149,129],{"emptyLinePlaceholder":128},[29,5151,5152,5154,5156,5158,5160],{"class":31,"line":132},[29,5153,334],{"class":111},[29,5155,4318],{"class":138},[29,5157,340],{"class":111},[29,5159,343],{"class":118},[29,5161,346],{"class":111},[12,5163,4214],{},[20,5165,5168],{"className":5166,"code":5167,"language":73,"meta":25},[71],"value == 10\n    ↓\n继续等待\n",[15,5169,5167],{"__ignoreMap":25},[12,5171,5172],{},"如果其他线程执行：",[20,5174,5176],{"className":22,"code":5175,"language":24,"meta":25,"style":25},"value.store(20);\nvalue.notify_one();\n",[15,5177,5178,5190],{"__ignoreMap":25},[29,5179,5180,5182,5184,5186,5188],{"class":31,"line":32},[29,5181,334],{"class":111},[29,5183,337],{"class":138},[29,5185,340],{"class":111},[29,5187,1001],{"class":118},[29,5189,346],{"class":111},[29,5191,5192,5194,5196],{"class":31,"line":125},[29,5193,334],{"class":111},[29,5195,4325],{"class":138},[29,5197,364],{"class":111},[12,5199,1778],{},[20,5201,5204],{"className":5202,"code":5203,"language":73,"meta":25},[71],"value != 10\n",[15,5205,5203],{"__ignoreMap":25},[12,5207,5208,5209,5212],{},"于是 ",[15,5210,5211],{},"wait(10)"," 返回。",[12,5214,5215],{},"所以：",[20,5217,5219],{"className":22,"code":5218,"language":24,"meta":25,"style":25},"wait(x)\n",[15,5220,5221],{"__ignoreMap":25},[29,5222,5223,5225],{"class":31,"line":32},[29,5224,4318],{"class":138},[29,5226,5227],{"class":111},"(x)\n",[12,5229,5230],{},"不要理解成：",[48,5232,5233],{},[12,5234,5235,5236],{},"等到值变成 ",[15,5237,1436],{},[12,5239,5240],{},"而应该理解成：",[48,5242,5243],{},[12,5244,5245],{},[83,5246,5247,5248,5250,5251,5253],{},"值还是 ",[15,5249,1436],{}," 的时候就等，等它不是 ",[15,5252,1436],{}," 了再继续。",[3048,5255],{},[93,5257,5259,5260,5263,5264,5266],{"id":5258},"和-condition_variable-有什么区别-学完condition_variable再回来看","和 ",[15,5261,5262],{},"condition_variable"," 有什么区别？ （学完",[15,5265,5262],{},"再回来看）",[12,5268,5269],{},"如果你的需求非常简单：",[20,5271,5274],{"className":5272,"code":5273,"language":73,"meta":25},[71],"ready 从 false 变成 true\n\nstate 从 0 变成其他值\n\ncounter 不再等于某个旧值\n",[15,5275,5273],{"__ignoreMap":25},[12,5277,5278],{},"直接：",[20,5280,5282],{"className":22,"code":5281,"language":24,"meta":25,"style":25},"atomic.wait()\natomic.notify_one()\natomic.notify_all()\n",[15,5283,5284,5292,5300],{"__ignoreMap":25},[29,5285,5286,5288,5290],{"class":31,"line":32},[29,5287,5104],{"class":111},[29,5289,4318],{"class":138},[29,5291,142],{"class":111},[29,5293,5294,5296,5298],{"class":31,"line":125},[29,5295,5104],{"class":111},[29,5297,4325],{"class":138},[29,5299,142],{"class":111},[29,5301,5302,5304,5306],{"class":31,"line":132},[29,5303,5104],{"class":111},[29,5305,4332],{"class":138},[29,5307,142],{"class":111},[12,5309,5310],{},"会非常方便。",[12,5312,5313],{},"但是如果你的等待条件比较复杂，例如：",[20,5315,5318],{"className":5316,"code":5317,"language":73,"meta":25},[71],"队列非空\n或者 finished == true\n或者 error != 0\n",[15,5319,5317],{"__ignoreMap":25},[12,5321,5322],{},"这种条件涉及多个变量和共享数据：",[20,5324,5326],{"className":22,"code":5325,"language":24,"meta":25,"style":25},"queue.empty()\nfinished\nerror\n",[15,5327,5328,5338,5343],{"__ignoreMap":25},[29,5329,5330,5333,5336],{"class":31,"line":32},[29,5331,5332],{"class":111},"queue.",[29,5334,5335],{"class":138},"empty",[29,5337,142],{"class":111},[29,5339,5340],{"class":31,"line":125},[29,5341,5342],{"class":111},"finished\n",[29,5344,5345],{"class":31,"line":132},[29,5346,5347],{"class":111},"error\n",[12,5349,5350],{},"通常还是使用：",[20,5352,5354],{"className":22,"code":5353,"language":24,"meta":25,"style":25},"std::condition_variable\n+\nstd::mutex\n+\n谓词\n",[15,5355,5356,5363,5368,5374,5378],{"__ignoreMap":25},[29,5357,5358,5360],{"class":31,"line":32},[29,5359,276],{"class":138},[29,5361,5362],{"class":111},"::condition_variable\n",[29,5364,5365],{"class":31,"line":125},[29,5366,5367],{"class":35},"+\n",[29,5369,5370,5372],{"class":31,"line":132},[29,5371,276],{"class":138},[29,5373,2523],{"class":111},[29,5375,5376],{"class":31,"line":145},[29,5377,5367],{"class":35},[29,5379,5380],{"class":31,"line":151},[29,5381,5382],{"class":111},"谓词\n",[12,5384,5385],{},"更加清楚。",[12,5387,5388],{},"因此可以简单记成：",[48,5390,5391],{},[12,5392,5393,5396,5397,5399],{},[15,5394,5395],{},"atomic::wait()"," 适合等待“一个原子变量的值发生变化”；",[15,5398,5262],{}," 更适合等待“复杂条件成立”。",[12,5401,5402,5403,5405],{},"最后把 ",[15,5404,4299],{}," 的核心记住就行：",[20,5407,5409],{"className":22,"code":5408,"language":24,"meta":25,"style":25},"value.wait(x);\n",[15,5410,5411],{"__ignoreMap":25},[29,5412,5413,5415,5417],{"class":31,"line":32},[29,5414,334],{"class":111},[29,5416,4318],{"class":138},[29,5418,5419],{"class":111},"(x);\n",[12,5421,5422],{},"等价于一句人话：",[48,5424,5425],{},[12,5426,5427],{},[83,5428,5429],{},"“只要 value 还是 x，我就继续等；什么时候不是 x 了，我什么时候继续执行。”",[88,5431,5432],{"id":5432},"选择与常见错误",[93,5434,5436],{"id":5435},"atomic-和-mutex-怎么选","atomic 和 mutex 怎么选",[5438,5439,5440,5454],"table",{},[5441,5442,5443],"thead",{},[5444,5445,5446,5451],"tr",{},[5447,5448,5450],"th",{"align":5449},"left","场景",[5447,5452,5453],{"align":5449},"推荐",[5455,5456,5457,5467,5476,5485,5494,5511,5520],"tbody",{},[5444,5458,5459,5463],{},[5460,5461,5462],"td",{"align":5449},"简单计数器",[5460,5464,5465],{"align":5449},[15,5466,5],{},[5444,5468,5469,5472],{},[5460,5470,5471],{"align":5449},"简单布尔状态",[5460,5473,5474],{"align":5449},[15,5475,1081],{},[5444,5477,5478,5481],{},[5460,5479,5480],{"align":5449},"一个指针或整数的独立状态切换",[5460,5482,5483],{"align":5449},[15,5484,5],{},[5444,5486,5487,5490],{},[5460,5488,5489],{"align":5449},"多个字段必须一起保持一致",[5460,5491,5492],{"align":5449},[15,5493,2328],{},[5444,5495,5496,5506],{},[5460,5497,5498,5499,2715,5502,5505],{"align":5449},"修改 ",[15,5500,5501],{},"vector",[15,5503,5504],{},"map"," 等复杂容器",[5460,5507,5508,5509],{"align":5449},"通常用 ",[15,5510,2328],{},[5444,5512,5513,5516],{},[5460,5514,5515],{"align":5449},"复杂业务逻辑临界区",[5460,5517,5518],{"align":5449},[15,5519,2328],{},[5444,5521,5522,5525],{},[5460,5523,5524],{"align":5449},"等待复杂条件",[5460,5526,5527,5530,5531],{"align":5449},[15,5528,5529],{},"std::condition_variable"," + ",[15,5532,464],{},[12,5534,5535],{},"不要为了追求“无锁”而强行把一个本来适合 mutex 的复合状态拆成很多 atomic。",[12,5537,5538],{},"代码正确性和可维护性通常比理论上的锁开销更重要。",[12,5540,5541],{},"一个实用判断是：",[20,5543,5546],{"className":5544,"code":5545,"language":73,"meta":25},[71],"能用一句话说清楚“这个 atomic 独立表示什么状态”\n→ 可以考虑 atomic\n\n需要解释好几个字段之间必须如何配合\n→ 通常先考虑 mutex\n",[15,5547,5545],{"__ignoreMap":25},[93,5549,5550],{"id":5550},"常见错误",[953,5552,5553,5559,5564,5567,5572,5575],{},[956,5554,5555,5556,5558],{},"把 ",[15,5557,5],{}," 理解成“一定 lock-free”。",[956,5560,5555,5561,5563],{},[15,5562,2654],{}," 当作线程同步工具。",[956,5565,5566],{},"用多个 atomic 表达一个必须整体一致的状态，却没有额外同步。",[956,5568,5569,5570,479],{},"在不理解内存模型时随意把所有操作改成 ",[15,5571,3159],{},[956,5573,5574],{},"自己实现自旋锁并在临界区长时间工作，导致 CPU 空转。",[956,5576,5577],{},"认为“atomic 变量安全”就等于“包含它的整个类都线程安全”。",[88,5579,5580],{"id":5580},"小结",[2696,5582,5583,5588,5598,5603,5609,5615,5622,5631],{},[956,5584,5585,5587],{},[15,5586,17],{}," 保证针对该对象的原子操作语义，但不保证所有实现一定 lock-free。",[956,5589,5590,401,5592,5594,5595,5597],{},[15,5591,298],{},[15,5593,302],{}," 负责原子读写；",[15,5596,472],{}," 等负责原子读改写。",[956,5599,5600,5602],{},[15,5601,946],{}," 可以原子替换并获得旧值。",[956,5604,5605,5608],{},[15,5606,5607],{},"compare_exchange_weak\u002Fstrong()"," 是 CAS 操作，也是很多无锁算法的基础。",[956,5610,5611,2655,5613,479],{},[15,5612,2654],{},[15,5614,63],{},[956,5616,5617,5618,5621],{},"默认内存序是 ",[15,5619,5620],{},"memory_order_seq_cst","，初学阶段优先使用默认值。",[956,5623,5624,2715,5626,2715,5628,5630],{},[15,5625,3151],{},[15,5627,3698],{},[15,5629,3694],{}," 属于更深入的内存模型内容，必须在理解同步关系后再使用。",[956,5632,5633,5634,5637],{},"C++20 的 ",[15,5635,5636],{},"atomic::wait\u002Fnotify"," 可以避免简单状态等待时的忙轮询。",[5639,5640,5641],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}",{"title":25,"searchDepth":125,"depth":125,"links":5643},[5644,5656,5663,5671,5682,5686],{"id":90,"depth":125,"text":91,"children":5645},[5646,5647,5649,5651,5652,5654],{"id":95,"depth":132,"text":95},{"id":294,"depth":132,"text":5648},"最基础的 load() 和 store()",{"id":468,"depth":132,"text":5650},"原子自增与 fetch_add()",{"id":943,"depth":132,"text":946},{"id":1078,"depth":132,"text":5653},"std::atomic\u003Cbool>：状态标记",{"id":1397,"depth":132,"text":5655},"atomic 不等于“整个对象线程安全”",{"id":1520,"depth":125,"text":1521,"children":5657},[5658,5660,5661],{"id":1524,"depth":132,"text":5659},"compare_exchange：比较并交换（CAS）",{"id":1928,"depth":132,"text":1931},{"id":2651,"depth":132,"text":5662},"volatile 不能替代 atomic",{"id":2733,"depth":125,"text":2733,"children":5664},[5665,5666,5668,5669,5670],{"id":2736,"depth":132,"text":2737},{"id":3052,"depth":132,"text":5667},"默认内存序：seq_cst",{"id":3159,"depth":132,"text":3159},{"id":3378,"depth":132,"text":3379},{"id":3713,"depth":132,"text":3713},{"id":3810,"depth":125,"text":3810,"children":5672},[5673,5674,5676,5678,5680],{"id":3813,"depth":132,"text":3816},{"id":4295,"depth":132,"text":5675},"C++20：wait() \u002F notify_one() \u002F notify_all()",{"id":4911,"depth":132,"text":5677},"notify_one() 和 notify_all()",{"id":5088,"depth":132,"text":5679},"wait() 到底在等什么？",{"id":5258,"depth":132,"text":5681},"和 condition_variable 有什么区别？ （学完condition_variable再回来看）",{"id":5432,"depth":125,"text":5432,"children":5683},[5684,5685],{"id":5435,"depth":132,"text":5436},{"id":5550,"depth":132,"text":5550},{"id":5580,"depth":125,"text":5580},"2000-1900-1300","2023-10-05","std::atomic\u003CT> 用来对一个共享对象执行原子操作，定义在：","2023-10-05-cplusplus-jiao-xue","\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue","Cplusplus教学","md",false,{},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1300-std-atomic",{"title":5,"description":5689},"wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1300-std-atomic","2NSOStiPltPravhpg2zlmnSkS6nkxbTVA5Zw6VxWvy8",[5701,5708,5714,5720,5726,5732,5738,5744,5750,5756,5762,5768,5774,5780,5786,5792,5798,5804,5810,5816,5822,5828,5834,5840,5846,5852,5858,5864,5870,5876,5882,5888,5894,5900,5906,5912,5918,5924,5930,5936,5942,5948,5954,5960,5966,5972,5978,5984,5990,5996,6002,6008,6014,6020,6026,6032,6038,6044,6050,6056,6062,6068,6074,6080,6082,6087,6093,6099,6105,6111,6117,6123,6129,6135,6141,6147,6153,6159,6165,6171,6177,6183,6189,6195,6201,6207,6213,6219,6225,6231,6237,6243],{"path":5702,"stem":5703,"title":5704,"authors":5705,"date":5688,"chapterOrder":5706,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0100-c-kai-fa-huan-jing-da-jian-yu-ce-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F0100-C++开发环境搭建与测试","C++开发环境搭建与测试",[7],"0100",0,{"path":5709,"stem":5710,"title":5711,"authors":5712,"date":5688,"chapterOrder":5713,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0200-c-ji-chu-chu-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F0200-C++基础初识","C++基础初识",[7],"0200",{"path":5715,"stem":5716,"title":5717,"authors":5718,"date":5688,"chapterOrder":5719,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0300-shu-ju-lei-xing-yu-shu-ju-cun-fang","wiki\u002F2023-10-05-Cplusplus教学\u002F0300-数据类型与数据存放","数据类型与数据存放",[7],"0300",{"path":5721,"stem":5722,"title":5723,"authors":5724,"date":5688,"chapterOrder":5725,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0400-shu-ru-shu-chu","wiki\u002F2023-10-05-Cplusplus教学\u002F0400-输入输出","输入输出",[7],"0400",{"path":5727,"stem":5728,"title":5729,"authors":5730,"date":5688,"chapterOrder":5731,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0500-yun-suan-fu","wiki\u002F2023-10-05-Cplusplus教学\u002F0500-运算符","运算符",[7],"0500",{"path":5733,"stem":5734,"title":5735,"authors":5736,"date":5688,"chapterOrder":5737,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0600-cheng-xu-liu-cheng-jie-gou","wiki\u002F2023-10-05-Cplusplus教学\u002F0600-程序流程结构","程序流程结构",[7],"0600",{"path":5739,"stem":5740,"title":5741,"authors":5742,"date":5688,"chapterOrder":5743,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0700-shu-zu","wiki\u002F2023-10-05-Cplusplus教学\u002F0700-数组","数组",[7],"0700",{"path":5745,"stem":5746,"title":5747,"authors":5748,"date":5688,"chapterOrder":5749,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0800-han-shu-yu-tou-wen-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F0800-函数与头文件","函数与头文件",[7],"0800",{"path":5751,"stem":5752,"title":5753,"authors":5754,"date":5688,"chapterOrder":5755,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0900-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F0900-指针","指针",[7],"0900",{"path":5757,"stem":5758,"title":5759,"authors":5760,"date":5688,"chapterOrder":5761,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1000-jie-gou-ti-yu-gong-yong-ti","wiki\u002F2023-10-05-Cplusplus教学\u002F1000-结构体与共用体","结构体与共用体",[7],"1000",{"path":5763,"stem":5764,"title":5765,"authors":5766,"date":5688,"chapterOrder":5767,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1100-nei-cun-fen-qu-yu-sheng-ming-zhou-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1100-内存分区与生命周期","内存分区与生命周期",[7],"1100",{"path":5769,"stem":5770,"title":5771,"authors":5772,"date":5688,"chapterOrder":5773,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1200-yin-yong","wiki\u002F2023-10-05-Cplusplus教学\u002F1200-引用","引用",[7],"1200",{"path":5775,"stem":5776,"title":5777,"authors":5778,"date":5688,"chapterOrder":5779,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1300-han-shu-ti-gao","wiki\u002F2023-10-05-Cplusplus教学\u002F1300-函数提高","函数提高",[7],"1300",{"path":5781,"stem":5782,"title":5783,"authors":5784,"date":5688,"chapterOrder":5785,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1350-ming-ming-kong-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F1350-命名空间","命名空间",[7],"1350",{"path":5787,"stem":5788,"title":5789,"authors":5790,"date":5688,"chapterOrder":5791,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0100-feng-zhuang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0100-封装","封装",[7],"1400-0100",{"path":5793,"stem":5794,"title":5795,"authors":5796,"date":5688,"chapterOrder":5797,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0200-dui-xiang-chu-shi-hua-he-qing-li","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0200-对象初始化和清理","对象初始化和清理",[7],"1400-0200",{"path":5799,"stem":5800,"title":5801,"authors":5802,"date":5688,"chapterOrder":5803,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0300-dui-xiang-mo-xing-yu-this-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0300-对象模型与this指针","对象模型与this指针",[7],"1400-0300",{"path":5805,"stem":5806,"title":5807,"authors":5808,"date":5688,"chapterOrder":5809,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0400-you-yuan-yu-yun-suan-fu-zhong-zai","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0400-友元与运算符重载","友元与运算符重载",[7],"1400-0400",{"path":5811,"stem":5812,"title":5813,"authors":5814,"date":5688,"chapterOrder":5815,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0500-ji-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0500-继承","继承",[7],"1400-0500",{"path":5817,"stem":5818,"title":5819,"authors":5820,"date":5688,"chapterOrder":5821,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0600-duo-tai","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0600-多态","多态",[7],"1400-0600",{"path":5823,"stem":5824,"title":5825,"authors":5826,"date":5688,"chapterOrder":5827,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-lei-he-dui-xiang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-类和对象","类和对象",[7],"1400",{"path":5829,"stem":5830,"title":5831,"authors":5832,"date":5688,"chapterOrder":5833,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1450-han-shu-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F1450-函数指针","函数指针",[7],"1450",{"path":5835,"stem":5836,"title":5837,"authors":5838,"date":5688,"chapterOrder":5839,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1500-wen-jian-cao-zuo","wiki\u002F2023-10-05-Cplusplus教学\u002F1500-文件操作","文件操作",[7],"1500",{"path":5841,"stem":5842,"title":5843,"authors":5844,"date":5688,"chapterOrder":5845,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1600-yi-chang-chu-li-yu-cuo-wu-ma","wiki\u002F2023-10-05-Cplusplus教学\u002F1600-异常处理与错误码","异常处理",[7],"1600",{"path":5847,"stem":5848,"title":5849,"authors":5850,"date":5688,"chapterOrder":5851,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1700-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F1700-模板","模板",[7],"1700",{"path":5853,"stem":5854,"title":5855,"authors":5856,"date":5688,"chapterOrder":5857,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0100-stl-chu-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0100-STL初识","STL初识",[7],"1800-0100",{"path":5859,"stem":5860,"title":5861,"authors":5862,"date":5688,"chapterOrder":5863,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0200-std-array","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0200-std-array","std::array",[7],"1800-0200",{"path":5865,"stem":5866,"title":5867,"authors":5868,"date":5688,"chapterOrder":5869,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0300-string-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0300-string容器","string容器",[7],"1800-0300",{"path":5871,"stem":5872,"title":5873,"authors":5874,"date":5688,"chapterOrder":5875,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0400-vector-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0400-vector容器","vector 容器",[7],"1800-0400",{"path":5877,"stem":5878,"title":5879,"authors":5880,"date":5688,"chapterOrder":5881,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0500-deque-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0500-deque容器","deque 容器",[7],"1800-0500",{"path":5883,"stem":5884,"title":5885,"authors":5886,"date":5688,"chapterOrder":5887,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0600-stack-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0600-stack容器","stack 容器",[7],"1800-0600",{"path":5889,"stem":5890,"title":5891,"authors":5892,"date":5688,"chapterOrder":5893,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0700-queue-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0700-queue容器","queue 容器",[7],"1800-0700",{"path":5895,"stem":5896,"title":5897,"authors":5898,"date":5688,"chapterOrder":5899,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0800-list-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0800-list容器","list 容器",[7],"1800-0800",{"path":5901,"stem":5902,"title":5903,"authors":5904,"date":5688,"chapterOrder":5905,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-0900-set-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-0900-set容器","set \u002F multiset 容器",[7],"1800-0900",{"path":5907,"stem":5908,"title":5909,"authors":5910,"date":5688,"chapterOrder":5911,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1000-map-rong-qi","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1000-map容器","map \u002F multimap 容器",[7],"1800-1000",{"path":5913,"stem":5914,"title":5915,"authors":5916,"date":5688,"chapterOrder":5917,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1100-han-shu-dui-xiang-yu-wei-ci","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1100-函数对象与谓词","函数对象与谓词",[7],"1800-1100",{"path":5919,"stem":5920,"title":5921,"authors":5922,"date":5688,"chapterOrder":5923,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1200-chang-yong-suan-fa","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1200-常用算法","常用算法",[7],"1800-1200",{"path":5925,"stem":5926,"title":5927,"authors":5928,"date":5688,"chapterOrder":5929,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-stl-ti-gao-bian-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-STL提高编程","STL提高编程",[7],"1800",{"path":5931,"stem":5932,"title":5933,"authors":5934,"date":5688,"chapterOrder":5935,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1900-c-lei-xing-zhuan-huan","wiki\u002F2023-10-05-Cplusplus教学\u002F1900-C++类型转换","C++类型转换",[7],"1900",{"path":5937,"stem":5938,"title":5939,"authors":5940,"date":5688,"chapterOrder":5941,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0100-auto","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0100-auto","auto",[7],"2000-0100",{"path":5943,"stem":5944,"title":5945,"authors":5946,"date":5688,"chapterOrder":5947,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0200-nullptr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0200-nullptr","nullptr",[7],"2000-0200",{"path":5949,"stem":5950,"title":5951,"authors":5952,"date":5688,"chapterOrder":5953,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0300-using","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0300-using","using",[7],"2000-0300",{"path":5955,"stem":5956,"title":5957,"authors":5958,"date":5688,"chapterOrder":5959,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0400-enum-class","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0400-enum-class","enum class",[7],"2000-0400",{"path":5961,"stem":5962,"title":5963,"authors":5964,"date":5688,"chapterOrder":5965,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0500-fan-wei-for-xun-huan","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0500-范围for循环","范围 for 循环",[7],"2000-0500",{"path":5967,"stem":5968,"title":5969,"authors":5970,"date":5688,"chapterOrder":5971,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0600-jie-gou-hua-bang-ding","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0600-结构化绑定","结构化绑定",[7],"2000-0600",{"path":5973,"stem":5974,"title":5975,"authors":5976,"date":5688,"chapterOrder":5977,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0700-constexpr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0700-constexpr","constexpr",[7],"2000-0700",{"path":5979,"stem":5980,"title":5981,"authors":5982,"date":5688,"chapterOrder":5983,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0800-raii","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0800-RAII","RAII",[7],"2000-0800",{"path":5985,"stem":5986,"title":5987,"authors":5988,"date":5688,"chapterOrder":5989,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0900-zhi-neng-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0900-智能指针","智能指针",[7],"2000-0900",{"path":5991,"stem":5992,"title":5993,"authors":5994,"date":5688,"chapterOrder":5995,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1000-you-zhi-yin-yong-he-yi-dong-yu-yi","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1000-右值引用和移动语义","右值引用和移动语义",[7],"2000-1000",{"path":5997,"stem":5998,"title":5999,"authors":6000,"date":5688,"chapterOrder":6001,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1100-lambda-biao-da-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1100-Lambda表达式","Lambda 表达式",[7],"2000-1100",{"path":6003,"stem":6004,"title":6005,"authors":6006,"date":5688,"chapterOrder":6007,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1200-std-function","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1200-std-function","std::function",[7],"2000-1200",{"path":6009,"stem":6010,"title":6011,"authors":6012,"date":5688,"chapterOrder":6013,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1300-std-bind","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1300-std-bind","std::bind",[7],"2000-1300",{"path":6015,"stem":6016,"title":6017,"authors":6018,"date":5688,"chapterOrder":6019,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1400-std-optional","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1400-std-optional","std::optional",[7],"2000-1400",{"path":6021,"stem":6022,"title":6023,"authors":6024,"date":5688,"chapterOrder":6025,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1500-std-variant","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1500-std-variant","std::variant",[7],"2000-1500",{"path":6027,"stem":6028,"title":6029,"authors":6030,"date":5688,"chapterOrder":6031,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1600-std-span","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1600-std-span","std::span",[7],"2000-1600",{"path":6033,"stem":6034,"title":6035,"authors":6036,"date":5688,"chapterOrder":6037,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1700-std-format-print","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1700-std-format-print","std::format \u002F std::print",[7],"2000-1700",{"path":6039,"stem":6040,"title":6041,"authors":6042,"date":5688,"chapterOrder":6043,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1800-std-chrono","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1800-std-chrono","std::chrono",[7],"2000-1800",{"path":6045,"stem":6046,"title":6047,"authors":6048,"date":5688,"chapterOrder":6049,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0100-bing-fa-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0100-并发是什么","并发是什么",[7],"2000-1900-0100",{"path":6051,"stem":6052,"title":6053,"authors":6054,"date":5688,"chapterOrder":6055,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0200-bing-xing-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0200-并行是什么","并行是什么",[7],"2000-1900-0200",{"path":6057,"stem":6058,"title":6059,"authors":6060,"date":5688,"chapterOrder":6061,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-0300-xie-cheng-shi-shen-me","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-0300-协程是什么","协程是什么",[7],"2000-1900-0300",{"path":6063,"stem":6064,"title":6065,"authors":6066,"date":5688,"chapterOrder":6067,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1000-bing-fa-bian-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1000-并发编程","并发编程",[7],"2000-1900-1000",{"path":6069,"stem":6070,"title":6071,"authors":6072,"date":5688,"chapterOrder":6073,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1100-std-thread","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1100-std-thread","std::thread",[7],"2000-1900-1100",{"path":6075,"stem":6076,"title":6077,"authors":6078,"date":5688,"chapterOrder":6079,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1200-mutex-lock-guard","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1200-mutex-lock-guard","mutex 与 RAII 锁",[7],"2000-1900-1200",{"path":5696,"stem":5698,"title":5,"authors":6081,"date":5688,"chapterOrder":5687,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},[7],{"path":6083,"stem":6084,"title":5262,"authors":6085,"date":5688,"chapterOrder":6086,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1400-condition-variable","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1400-condition-variable",[7],"2000-1900-1400",{"path":6088,"stem":6089,"title":6090,"authors":6091,"date":5688,"chapterOrder":6092,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1500-future-async-promise","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1500-future-async-promise","future、async、promise 与 packaged_task",[7],"2000-1900-1500",{"path":6094,"stem":6095,"title":6096,"authors":6097,"date":5688,"chapterOrder":6098,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1600-jthread-stop-token","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1600-jthread-stop-token","jthread 与 stop_token",[7],"2000-1900-1600",{"path":6100,"stem":6101,"title":6102,"authors":6103,"date":5688,"chapterOrder":6104,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1700-semaphore-latch-barrier","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1700-semaphore-latch-barrier","semaphore、latch 与 barrier",[7],"2000-1900-1700",{"path":6106,"stem":6107,"title":6108,"authors":6109,"date":5688,"chapterOrder":6110,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-bing-fa-bing-xing-yu-xie-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-并发并行与协程","并发、并行与协程",[7],"2000-1900",{"path":6112,"stem":6113,"title":6114,"authors":6115,"date":5688,"chapterOrder":6116,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-2000-std-filesystem","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-2000-std-filesystem","std::filesystem",[7],"2000-2000",{"path":6118,"stem":6119,"title":6120,"authors":6121,"date":5688,"chapterOrder":6122,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-2100-modules-jian-jie","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-2100-modules简介","modules 简介",[7],"2000-2100",{"path":6124,"stem":6125,"title":6126,"authors":6127,"date":5688,"chapterOrder":6128,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-xian-dai-c","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-现代C++","现代C++",[7],"2000",{"path":6130,"stem":6131,"title":6132,"authors":6133,"date":5688,"chapterOrder":6134,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0100-ding-shi-qi-yu-yi-bu-io","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0100-定时器与异步IO","定时器与异步 IO",[7],"2100-0100-0100",{"path":6136,"stem":6137,"title":6138,"authors":6139,"date":5688,"chapterOrder":6140,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0200-boost-asio-ji-chu","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0200-Boost.Asio基础","Boost.Asio 基础",[7],"2100-0100-0200",{"path":6142,"stem":6143,"title":6144,"authors":6145,"date":5688,"chapterOrder":6146,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0300-chuan-kou-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0300-串口通信","串口通信教学",[7],"2100-0100-0300",{"path":6148,"stem":6149,"title":6150,"authors":6151,"date":5688,"chapterOrder":6152,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0350-chuan-kou-tong-xin-chuan-kou-bao-xie-yi-shi-li","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0350-串口通信串口包协议实例","串口通信串口包协议实例",[7],"2100-0100-0350",{"path":6154,"stem":6155,"title":6156,"authors":6157,"date":5688,"chapterOrder":6158,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0400-tcp-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0400-TCP通信","TCP 通信",[7],"2100-0100-0400",{"path":6160,"stem":6161,"title":6162,"authors":6163,"date":5688,"chapterOrder":6164,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0500-udp-tong-xin","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0500-UDP通信","UDP 通信",[7],"2100-0100-0500",{"path":6166,"stem":6167,"title":6168,"authors":6169,"date":5688,"chapterOrder":6170,"chapterDepth":125,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-0600-ji-qi-ren-gong-cheng-xie-fa-yu-ros2-ji-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-0600-机器人工程写法与ROS2集成","机器人工程写法与 ROS2 集成",[7],"2100-0100-0600",{"path":6172,"stem":6173,"title":6174,"authors":6175,"date":5688,"chapterOrder":6176,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0100-boost-asio-yi-bu-io-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0100-Boost.Asio异步IO库","Boost.Asio异步IO库",[7],"2100-0100",{"path":6178,"stem":6179,"title":6180,"authors":6181,"date":5688,"chapterOrder":6182,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0200-eigen-xian-xing-dai-shu-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0200-Eigen线性代数库","Eigen线性代数库",[7],"2100-0200",{"path":6184,"stem":6185,"title":6186,"authors":6187,"date":5688,"chapterOrder":6188,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0300-opencv-ji-suan-ji-shi-jue-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0300-OpenCV计算机视觉库","OpenCV计算机视觉库",[7],"2100-0300",{"path":6190,"stem":6191,"title":6192,"authors":6193,"date":5688,"chapterOrder":6194,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0400-pcl-dian-yun-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0400-PCL点云库","PCL点云库",[7],"2100-0400",{"path":6196,"stem":6197,"title":6198,"authors":6199,"date":5688,"chapterOrder":6200,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-0500-sophus-li-qun-li-dai-shu-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-0500-Sophus李群李代数库","Sophus 李群李代数库",[7],"2100-0500",{"path":6202,"stem":6203,"title":6204,"authors":6205,"date":5688,"chapterOrder":6206,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-chang-yong-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-常用库","常用库学习",[7],"2100",{"path":6208,"stem":6209,"title":6210,"authors":6211,"date":5688,"chapterOrder":6212,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0100-cmakepresets-yu-gou-jian-an-zhuang","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0100-CMakePresets与构建安装","CMakePresets与构建安装",[7],"2200-0100",{"path":6214,"stem":6215,"title":6216,"authors":6217,"date":5688,"chapterOrder":6218,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0200-ding-ceng-cmake-yu-gong-gong-bian-yi-xuan-xiang","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0200-顶层CMake与公共编译选项","顶层CMake与公共编译选项",[7],"2200-0200",{"path":6220,"stem":6221,"title":6222,"authors":6223,"date":5688,"chapterOrder":6224,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0300-src-yu-lib-mo-kuai-cmake-xiang-jie","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0300-src与lib模块CMake详解","src与lib模块CMake详解",[7],"2200-0300",{"path":6226,"stem":6227,"title":6228,"authors":6229,"date":5688,"chapterOrder":6230,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0400-di-san-fang-ku-yi-lai-xie-fa","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0400-第三方库依赖写法","第三方库依赖写法",[7],"2200-0400",{"path":6232,"stem":6233,"title":6234,"authors":6235,"date":5688,"chapterOrder":6236,"chapterDepth":32,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-0500-mu-ban-kuo-zhan-yu-chang-jian-wen-ti","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-0500-模板扩展与常见问题","模板扩展与常见问题",[7],"2200-0500",{"path":6238,"stem":6239,"title":6240,"authors":6241,"date":5688,"chapterOrder":6242,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":5694},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-cmake-gong-cheng-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-CMake工程模板","CMake工程模板",[7],"2200",{"path":5691,"stem":6244,"title":6245,"authors":6246,"date":5688,"chapterOrder":6247,"chapterDepth":5707,"docKey":5690,"docRoot":5691,"docTitle":5692,"isWikiDoc":128,"isWikiIndex":128},"wiki\u002F2023-10-05-Cplusplus教学\u002Findex","C\u002FC++教程",[7],null,[6249,6251,6254,6257],{"name":7,"image":6250},"\u002Fimages\u002Fmember_photo\u002F董佳辉.jpg",{"name":6252,"image":6253},"宫金良","\u002Fimages\u002Fmember_photo\u002F宫金良.jpg",{"name":6255,"image":6256},"巩丽","\u002Fimages\u002Fmember_photo\u002F巩丽.png",{"name":6258,"image":6259},"张彦斐","\u002Fimages\u002Fmember_photo\u002F张彦斐.jpg",1792431899088]