[{"data":1,"prerenderedAt":8567},["ShallowReactive",2],{"wiki-page-\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1200-mutex-lock-guard":3,"wiki-doc-items-2023-10-05-cplusplus-jiao-xue":8008,"wiki:author-members":8555},{"id":4,"title":5,"authors":6,"body":8,"chapterDepth":172,"chapterOrder":7995,"date":7996,"description":7997,"docKey":7998,"docRoot":7999,"docTitle":8000,"extension":8001,"isWikiDoc":183,"isWikiIndex":8002,"meta":8003,"navigation":183,"path":8004,"seo":8005,"stem":8006,"wikiDepth":37,"__hash__":8007},"wiki\u002Fwiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1200-mutex-lock-guard.md","mutex 与 RAII 锁",[7],"董佳辉",{"type":9,"value":10,"toc":7909},"minimark",[11,20,23,46,49,55,58,63,66,145,148,152,155,394,399,407,410,416,419,430,433,436,442,445,451,454,457,467,469,477,480,490,493,499,502,505,511,514,520,523,533,536,542,545,551,554,560,563,566,571,573,576,579,590,593,598,601,611,614,617,623,626,629,634,637,642,645,650,653,658,660,663,666,676,679,688,691,696,699,704,707,713,716,726,729,753,756,758,764,770,773,779,782,787,790,793,799,802,808,811,817,820,826,829,835,838,841,847,849,853,856,858,882,885,893,896,903,906,936,939,949,952,955,960,963,969,972,984,990,992,998,1003,1015,1018,1048,1050,1057,1060,1073,1076,1082,1085,1091,1094,1107,1110,1113,1124,1127,1132,1134,1140,1143,1155,1158,1170,1172,1207,1210,1216,1219,1221,1227,1230,1236,1239,1245,1248,1259,1261,1326,1329,1335,1338,1344,1347,1356,1359,1368,1370,1398,1404,1407,1425,1428,1433,1436,1441,1443,1449,1452,1666,1671,1676,1682,1689,1692,1700,1703,1717,1720,1726,1729,1732,1754,1757,1759,1767,1770,1806,1809,1874,1876,1887,1890,1902,1905,1908,1920,1923,1926,1929,1941,1944,1947,1983,1985,1996,1999,2011,2014,2017,2034,2037,2042,2045,2056,2059,2061,2065,2069,2072,2077,2080,2083,2089,2092,2124,2127,2133,2136,2147,2150,2155,2157,2162,2165,2177,2179,2241,2244,2279,2282,2288,2291,2297,2299,2357,2360,2372,2374,2381,2384,2591,2595,2600,2603,2609,2612,2622,2625,2628,2650,2653,2673,2675,2683,2686,2689,2694,2700,2773,2776,2782,2784,2796,2799,2801,2804,2807,2864,2867,2873,2876,2882,2885,2908,2911,2914,2970,2973,2978,2981,3003,3006,3008,3011,3014,3019,3021,3041,3044,3046,3066,3069,3111,3114,3177,3180,3186,3189,3191,3196,3198,3202,3205,3242,3244,3259,3262,3279,3282,3343,3346,3352,3355,3360,3363,3368,3370,3374,3377,3433,3436,3442,3445,3553,3556,3565,3568,3571,3577,3580,3586,3589,3594,3597,3603,3605,3611,3616,3622,3625,3627,3633,3636,3648,3651,3683,3689,3695,3701,3703,3709,3711,3767,3769,3775,3781,3783,3789,3791,3867,3873,3879,3882,3885,3887,3894,3897,3918,3921,3924,3955,3958,3964,3967,3980,3982,4043,4045,4052,4055,4085,4088,4091,4138,4140,4145,4151,4160,4163,4165,4193,4196,4209,4212,4220,4222,4234,4237,4249,4252,4260,4263,4276,4279,4287,4289,4297,4300,4347,4350,4356,4359,4365,4368,4373,4379,4387,4390,4393,4395,4514,4517,4520,4523,4526,4529,4542,4544,4563,4566,4612,4615,4657,4660,4666,4669,4675,4677,4683,4686,4688,4692,4694,4697,4700,4703,4709,4712,4715,4717,4723,4726,4728,4734,4737,4739,4743,4746,4751,4754,4760,4763,4804,4807,4813,4816,4822,4825,4827,4832,4835,4840,4843,4855,4857,4926,4929,4932,4968,4971,4977,4980,5006,5009,5012,5018,5020,5026,5029,5032,5053,5056,5077,5080,5083,5089,5091,5097,5100,5115,5118,5120,5134,5137,5143,5146,5148,5216,5218,5230,5236,5244,5246,5256,5259,5264,5267,5273,5276,5300,5303,5305,5374,5378,5383,5386,5396,5399,5401,5475,5478,5484,5487,5490,5495,5498,5510,5512,5524,5527,5530,5538,5541,5547,5550,5553,5555,5560,5562,5574,5577,5582,5585,5587,5593,5595,5607,5610,5629,5631,5736,5739,5750,5753,5758,5760,5766,5768,5779,5781,5786,5789,5808,5811,5813,5819,5822,5827,5830,5842,5845,5851,5854,5857,5863,5865,5871,5874,5877,5883,5886,5892,5895,5907,5910,5915,5917,5925,5932,5935,5947,5950,5960,5962,6119,6122,6128,6131,6134,6156,6159,6161,6167,6169,6174,6176,6179,6182,6258,6261,6266,6269,6271,6277,6280,6286,6289,6291,6296,6299,6301,6307,6310,6316,6319,6324,6330,6336,6339,6341,6351,6354,6360,6363,6366,6372,6375,6377,6380,6385,6388,6393,6395,6407,6410,6416,6419,6425,6428,6431,6450,6452,6636,6640,6646,6649,6664,6666,6676,6679,6682,6688,6691,6693,6701,6704,6749,6751,6762,6765,6768,6773,6776,6787,6789,6793,6797,6800,6806,6809,6814,6817,6822,6824,6968,6971,6986,6989,6995,6998,7000,7008,7010,7025,7028,7031,7053,7056,7059,7072,7075,7083,7086,7089,7101,7104,7107,7110,7119,7122,7124,7127,7129,7161,7163,7174,7177,7186,7189,7192,7197,7200,7206,7209,7211,7215,7218,7224,7230,7236,7239,7245,7247,7250,7253,7323,7326,7328,7336,7338,7388,7391,7393,7396,7401,7404,7406,7409,7411,7463,7466,7468,7471,7474,7480,7483,7485,7488,7520,7523,7525,7529,7532,7535,7541,7544,7549,7551,7555,7558,7576,7579,7585,7588,7591,7597,7600,7602,7609,7614,7617,7623,7626,7628,7632,7634,7645,7648,7653,7656,7661,7663,7683,7685,7691,7694,7700,7703,7705,7708,7713,7718,7721,7744,7747,7780,7783,7786,7796,7799,7809,7812,7822,7825,7835,7838,7848,7851,7861,7864,7874,7877,7883,7886,7892,7895,7905],[12,13,14,15,19],"p",{},"多个线程同时运行时，如果它们会访问同一份",[16,17,18],"strong",{},"共享数据","，并且其中至少有一个线程会修改这份数据，就必须开始考虑线程同步问题。",[12,21,22],{},"C++ 中最基础、最常用的同步工具之一就是：",[24,25,30],"pre",{"className":26,"code":27,"language":28,"meta":29,"style":29},"language-cpp shiki shiki-themes github-light github-dark","std::mutex\n","cpp","",[31,32,33],"code",{"__ignoreMap":29},[34,35,38,42],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"sScJk","std",[34,43,45],{"class":44},"sVt8B","::mutex\n",[12,47,48],{},"mutex 的中文通常叫：",[50,51,52],"blockquote",{},[12,53,54],{},"互斥量",[12,56,57],{},"它最核心的作用可以简单理解成：",[50,59,60],{},[12,61,62],{},"同一时刻只允许一个线程进入某段需要保护的代码。",[12,64,65],{},"这一节重点学习：",[67,68,69,73,79,92,100,103,109,115,121,124,133,139],"ul",{},[70,71,72],"li",{},"什么是共享数据、数据竞争和临界区；",[70,74,75,78],{},[31,76,77],{},"std::mutex"," 到底“锁”的是什么；",[70,80,81,84,85,84,88,91],{},[31,82,83],{},"lock()","、",[31,86,87],{},"try_lock()",[31,89,90],{},"unlock()","；",[70,93,94,95,97,98,91],{},"为什么不推荐手动成对调用 ",[31,96,83],{}," \u002F ",[31,99,90],{},[70,101,102],{},"RAII 为什么特别适合管理锁；",[70,104,105,108],{},[31,106,107],{},"std::lock_guard"," 的基本使用；",[70,110,111,114],{},[31,112,113],{},"std::unique_lock"," 为什么更加灵活；",[70,116,117,120],{},[31,118,119],{},"std::scoped_lock"," 为什么适合多把锁；",[70,122,123],{},"什么是死锁，以及怎样避免；",[70,125,126,129,130,91],{},[31,127,128],{},"std::recursive_mutex"," 和 ",[31,131,132],{},"std::timed_mutex",[70,134,135,138],{},[31,136,137],{},"std::shared_mutex"," 的多读单写；",[70,140,141,144],{},[31,142,143],{},"std::call_once"," 的一次性初始化。",[146,147],"hr",{},[149,150,151],"h2",{"id":151},"为什么需要互斥量",[12,153,154],{},"先来看一个最经典的例子：",[24,156,158],{"className":26,"code":157,"language":28,"meta":29,"style":29},"#include \u003Cprint>\n#include \u003Cthread>\n\nint 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::thread t1(add_many);\n    std::thread t2(add_many);\n\n    t1.join();\n    t2.join();\n\n    std::println(\"{}\", counter);\n}\n",[31,159,160,170,178,185,204,209,221,227,263,269,278,284,290,295,305,310,325,337,342,354,364,369,389],{"__ignoreMap":29},[34,161,162,166],{"class":36,"line":37},[34,163,165],{"class":164},"szBVR","#include",[34,167,169],{"class":168},"sZZnC"," \u003Cprint>\n",[34,171,173,175],{"class":36,"line":172},2,[34,174,165],{"class":164},[34,176,177],{"class":168}," \u003Cthread>\n",[34,179,181],{"class":36,"line":180},3,[34,182,184],{"emptyLinePlaceholder":183},true,"\n",[34,186,188,191,194,197,201],{"class":36,"line":187},4,[34,189,190],{"class":164},"int",[34,192,193],{"class":44}," counter ",[34,195,196],{"class":164},"=",[34,198,200],{"class":199},"sj4cs"," 0",[34,202,203],{"class":44},";\n",[34,205,207],{"class":36,"line":206},5,[34,208,184],{"emptyLinePlaceholder":183},[34,210,212,215,218],{"class":36,"line":211},6,[34,213,214],{"class":164},"void",[34,216,217],{"class":40}," add_many",[34,219,220],{"class":44},"()\n",[34,222,224],{"class":36,"line":223},7,[34,225,226],{"class":44},"{\n",[34,228,230,233,236,238,241,243,245,248,251,254,257,260],{"class":36,"line":229},8,[34,231,232],{"class":164},"    for",[34,234,235],{"class":44}," (",[34,237,190],{"class":164},[34,239,240],{"class":44}," i ",[34,242,196],{"class":164},[34,244,200],{"class":199},[34,246,247],{"class":44},"; i ",[34,249,250],{"class":164},"\u003C",[34,252,253],{"class":199}," 100000",[34,255,256],{"class":44},"; ",[34,258,259],{"class":164},"++",[34,261,262],{"class":44},"i)\n",[34,264,266],{"class":36,"line":265},9,[34,267,268],{"class":44},"    {\n",[34,270,272,275],{"class":36,"line":271},10,[34,273,274],{"class":164},"        ++",[34,276,277],{"class":44},"counter;\n",[34,279,281],{"class":36,"line":280},11,[34,282,283],{"class":44},"    }\n",[34,285,287],{"class":36,"line":286},12,[34,288,289],{"class":44},"}\n",[34,291,293],{"class":36,"line":292},13,[34,294,184],{"emptyLinePlaceholder":183},[34,296,298,300,303],{"class":36,"line":297},14,[34,299,190],{"class":164},[34,301,302],{"class":40}," main",[34,304,220],{"class":44},[34,306,308],{"class":36,"line":307},15,[34,309,226],{"class":44},[34,311,313,316,319,322],{"class":36,"line":312},16,[34,314,315],{"class":40},"    std",[34,317,318],{"class":44},"::thread ",[34,320,321],{"class":40},"t1",[34,323,324],{"class":44},"(add_many);\n",[34,326,328,330,332,335],{"class":36,"line":327},17,[34,329,315],{"class":40},[34,331,318],{"class":44},[34,333,334],{"class":40},"t2",[34,336,324],{"class":44},[34,338,340],{"class":36,"line":339},18,[34,341,184],{"emptyLinePlaceholder":183},[34,343,345,348,351],{"class":36,"line":344},19,[34,346,347],{"class":44},"    t1.",[34,349,350],{"class":40},"join",[34,352,353],{"class":44},"();\n",[34,355,357,360,362],{"class":36,"line":356},20,[34,358,359],{"class":44},"    t2.",[34,361,350],{"class":40},[34,363,353],{"class":44},[34,365,367],{"class":36,"line":366},21,[34,368,184],{"emptyLinePlaceholder":183},[34,370,372,374,377,380,383,386],{"class":36,"line":371},22,[34,373,315],{"class":40},[34,375,376],{"class":44},"::",[34,378,379],{"class":40},"println",[34,381,382],{"class":44},"(",[34,384,385],{"class":168},"\"{}\"",[34,387,388],{"class":44},", counter);\n",[34,390,392],{"class":36,"line":391},23,[34,393,289],{"class":44},[12,395,396],{},[16,397,398],{},"运行结果示例（存在数据竞争，实际数字每次可能不同）：",[24,400,405],{"className":401,"code":403,"language":404,"meta":29},[402],"language-text","104563\n","text",[31,406,403],{"__ignoreMap":29},[12,408,409],{},"这里创建了两个线程：",[24,411,414],{"className":412,"code":413,"language":404,"meta":29},[402],"线程 t1\n线程 t2\n",[31,415,413],{"__ignoreMap":29},[12,417,418],{},"每个线程都执行：",[24,420,422],{"className":26,"code":421,"language":28,"meta":29,"style":29},"++counter;\n",[31,423,424],{"__ignoreMap":29},[34,425,426,428],{"class":36,"line":37},[34,427,259],{"class":164},[34,429,277],{"class":44},[12,431,432],{},"100000 次。",[12,434,435],{},"直觉上：",[24,437,440],{"className":438,"code":439,"language":404,"meta":29},[402],"100000 + 100000 = 200000\n",[31,441,439],{"__ignoreMap":29},[12,443,444],{},"所以好像最后一定应该输出：",[24,446,449],{"className":447,"code":448,"language":404,"meta":29},[402],"200000\n",[31,450,448],{"__ignoreMap":29},[12,452,453],{},"但实际上，这段程序存在严重的并发问题。",[12,455,456],{},"问题就在：",[24,458,459],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,460,461],{"__ignoreMap":29},[34,462,463,465],{"class":36,"line":37},[34,464,259],{"class":164},[34,466,277],{"class":44},[146,468],{},[470,471,473,476],"h3",{"id":472},"counter-并不是不可分割的一步",[31,474,475],{},"++counter"," 并不是不可分割的一步",[12,478,479],{},"我们平时看：",[24,481,482],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,483,484],{"__ignoreMap":29},[34,485,486,488],{"class":36,"line":37},[34,487,259],{"class":164},[34,489,277],{"class":44},[12,491,492],{},"感觉它就是：",[24,494,497],{"className":495,"code":496,"language":404,"meta":29},[402],"counter 加 1\n",[31,498,496],{"__ignoreMap":29},[12,500,501],{},"一条操作。",[12,503,504],{},"但从程序执行的角度来看，它通常大致需要经历：",[24,506,509],{"className":507,"code":508,"language":404,"meta":29},[402],"读取 counter 当前值\n        ↓\n当前值 + 1\n        ↓\n把结果写回 counter\n",[31,510,508],{"__ignoreMap":29},[12,512,513],{},"假设现在：",[24,515,518],{"className":516,"code":517,"language":404,"meta":29},[402],"counter = 10\n",[31,519,517],{"__ignoreMap":29},[12,521,522],{},"两个线程同时执行：",[24,524,525],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,526,527],{"__ignoreMap":29},[34,528,529,531],{"class":36,"line":37},[34,530,259],{"class":164},[34,532,277],{"class":44},[12,534,535],{},"就可能出现：",[24,537,540],{"className":538,"code":539,"language":404,"meta":29},[402],"线程 A：读取 counter，得到 10\n线程 B：读取 counter，得到 10\n\n线程 A：计算 10 + 1 = 11\n线程 B：计算 10 + 1 = 11\n\n线程 A：写回 11\n线程 B：写回 11\n",[31,541,539],{"__ignoreMap":29},[12,543,544],{},"最终：",[24,546,549],{"className":547,"code":548,"language":404,"meta":29},[402],"counter = 11\n",[31,550,548],{"__ignoreMap":29},[12,552,553],{},"但两个线程明明都执行了一次加法，正确结果应该是：",[24,555,558],{"className":556,"code":557,"language":404,"meta":29},[402],"counter = 12\n",[31,559,557],{"__ignoreMap":29},[12,561,562],{},"其中一次修改就这样丢失了。",[12,564,565],{},"这说明：",[50,567,568],{},[12,569,570],{},"即使 C++ 源代码里看起来只有一条语句，也不代表它在多线程环境中天然不可被其他线程干扰。",[146,572],{},[470,574,575],{"id":575},"什么是数据竞争",[12,577,578],{},"如果：",[67,580,581,584,587],{},[70,582,583],{},"多个线程访问同一个内存位置；",[70,585,586],{},"至少有一个线程会写；",[70,588,589],{},"这些访问之间没有正确的同步；",[12,591,592],{},"就可能产生：",[50,594,595],{},[12,596,597],{},"数据竞争（Data Race）",[12,599,600],{},"前面的：",[24,602,603],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,604,605],{"__ignoreMap":29},[34,606,607,609],{"class":36,"line":37},[34,608,259],{"class":164},[34,610,277],{"class":44},[12,612,613],{},"就是典型例子。",[12,615,616],{},"线程 A 和线程 B 都在：",[24,618,621],{"className":619,"code":620,"language":404,"meta":29},[402],"读取 counter\n修改 counter\n",[31,622,620],{"__ignoreMap":29},[12,624,625],{},"并且没有任何同步措施。",[12,627,628],{},"在 C++ 中，数据竞争通常意味着：",[50,630,631],{},[12,632,633],{},"未定义行为（Undefined Behavior）",[12,635,636],{},"所以不能只理解成：",[50,638,639],{},[12,640,641],{},"“最后结果偶尔少 1。”",[12,643,644],{},"真正的问题是：",[50,646,647],{},[12,648,649],{},"一旦发生数据竞争，C++ 标准就不再保证程序行为。",[12,651,652],{},"因此多线程编程中第一件非常重要的事情就是：",[50,654,655],{},[12,656,657],{},"不要让多个线程无保护地同时访问共享可变数据。",[146,659],{},[470,661,662],{"id":662},"什么是临界区",[12,664,665],{},"假设有：",[24,667,668],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,669,670],{"__ignoreMap":29},[34,671,672,674],{"class":36,"line":37},[34,673,259],{"class":164},[34,675,277],{"class":44},[12,677,678],{},"这段代码访问了多个线程共享的变量：",[24,680,682],{"className":26,"code":681,"language":28,"meta":29,"style":29},"counter\n",[31,683,684],{"__ignoreMap":29},[34,685,686],{"class":36,"line":37},[34,687,681],{"class":44},[12,689,690],{},"并且我们希望：",[50,692,693],{},[12,694,695],{},"同一时刻只能有一个线程执行它。",[12,697,698],{},"那么这段代码就可以称为：",[50,700,701],{},[12,702,703],{},"临界区（Critical Section）",[12,705,706],{},"简单理解：",[24,708,711],{"className":709,"code":710,"language":404,"meta":29},[402],"临界区\n=\n不能让多个线程同时执行的代码区域\n",[31,712,710],{"__ignoreMap":29},[12,714,715],{},"例如：",[24,717,718],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,719,720],{"__ignoreMap":29},[34,721,722,724],{"class":36,"line":37},[34,723,259],{"class":164},[34,725,277],{"class":44},[12,727,728],{},"也可能是一整组操作：",[24,730,732],{"className":26,"code":731,"language":28,"meta":29,"style":29},"++count;\naverage = calculate_average();\n",[31,733,734,741],{"__ignoreMap":29},[34,735,736,738],{"class":36,"line":37},[34,737,259],{"class":164},[34,739,740],{"class":44},"count;\n",[34,742,743,746,748,751],{"class":36,"line":172},[34,744,745],{"class":44},"average ",[34,747,196],{"class":164},[34,749,750],{"class":40}," calculate_average",[34,752,353],{"class":44},[12,754,755],{},"只要这些操作必须作为一个整体被保护，都可以属于同一个临界区。",[146,757],{},[149,759,761,763],{"id":760},"stdmutex-基础",[31,762,77],{}," 基础",[470,765,767,769],{"id":766},"stdmutex-到底是什么",[31,768,77],{}," 到底是什么",[12,771,772],{},"mutex 是：",[24,774,777],{"className":775,"code":776,"language":404,"meta":29},[402],"mutual exclusion\n",[31,778,776],{"__ignoreMap":29},[12,780,781],{},"也就是：",[50,783,784],{},[12,785,786],{},"互斥",[12,788,789],{},"可以把它想象成一把只有一把钥匙的门锁。",[12,791,792],{},"假设有一个房间：",[24,794,797],{"className":795,"code":796,"language":404,"meta":29},[402],"共享数据\n",[31,798,796],{"__ignoreMap":29},[12,800,801],{},"门口放着：",[24,803,806],{"className":804,"code":805,"language":404,"meta":29},[402],"一把 mutex\n",[31,807,805],{"__ignoreMap":29},[12,809,810],{},"线程 A 想进去：",[24,812,815],{"className":813,"code":814,"language":404,"meta":29},[402],"线程 A\n  ↓\n尝试获得 mutex\n  ↓\n成功\n  ↓\n进入临界区\n",[31,816,814],{"__ignoreMap":29},[12,818,819],{},"此时线程 B 也想进去：",[24,821,824],{"className":822,"code":823,"language":404,"meta":29},[402],"线程 B\n  ↓\n尝试获得 mutex\n  ↓\n发现 mutex 已经被 A 持有\n  ↓\n等待\n",[31,825,823],{"__ignoreMap":29},[12,827,828],{},"等线程 A 离开：",[24,830,833],{"className":831,"code":832,"language":404,"meta":29},[402],"线程 A 释放 mutex\n",[31,834,832],{"__ignoreMap":29},[12,836,837],{},"线程 B 才有机会获得它。",[12,839,840],{},"所以 mutex 的核心效果就是：",[24,842,845],{"className":843,"code":844,"language":404,"meta":29},[402],"线程 A ─┐\n线程 B ─┼→ 同一时刻只能有一个线程通过\n线程 C ─┘\n",[31,846,844],{"__ignoreMap":29},[146,848],{},[470,850,852],{"id":851},"mutex-并不会自动锁住变量","mutex 并不会自动“锁住变量”",[12,854,855],{},"这是初学 mutex 时非常容易误解的一点。",[12,857,715],{},[24,859,861],{"className":26,"code":860,"language":28,"meta":29,"style":29},"int counter = 0;\nstd::mutex mutex;\n",[31,862,863,875],{"__ignoreMap":29},[34,864,865,867,869,871,873],{"class":36,"line":37},[34,866,190],{"class":164},[34,868,193],{"class":44},[34,870,196],{"class":164},[34,872,200],{"class":199},[34,874,203],{"class":44},[34,876,877,879],{"class":36,"line":172},[34,878,41],{"class":40},[34,880,881],{"class":44},"::mutex mutex;\n",[12,883,884],{},"mutex 并不知道：",[24,886,887],{"className":26,"code":681,"language":28,"meta":29,"style":29},[31,888,889],{"__ignoreMap":29},[34,890,891],{"class":36,"line":37},[34,892,681],{"class":44},[12,894,895],{},"的存在。",[12,897,898,899,902],{},"它和 ",[31,900,901],{},"counter"," 之间没有任何自动绑定关系。",[12,904,905],{},"例如线程 A：",[24,907,909],{"className":26,"code":908,"language":28,"meta":29,"style":29},"mutex.lock();\n++counter;\nmutex.unlock();\n",[31,910,911,921,927],{"__ignoreMap":29},[34,912,913,916,919],{"class":36,"line":37},[34,914,915],{"class":44},"mutex.",[34,917,918],{"class":40},"lock",[34,920,353],{"class":44},[34,922,923,925],{"class":36,"line":172},[34,924,259],{"class":164},[34,926,277],{"class":44},[34,928,929,931,934],{"class":36,"line":180},[34,930,915],{"class":44},[34,932,933],{"class":40},"unlock",[34,935,353],{"class":44},[12,937,938],{},"但线程 B 如果直接：",[24,940,941],{"className":26,"code":421,"language":28,"meta":29,"style":29},[31,942,943],{"__ignoreMap":29},[34,944,945,947],{"class":36,"line":37},[34,946,259],{"class":164},[34,948,277],{"class":44},[12,950,951],{},"mutex 根本拦不住它。",[12,953,954],{},"所以更准确的理解是：",[50,956,957],{},[12,958,959],{},"mutex 不是把某个变量锁住，而是让所有遵守同一套加锁规则的线程无法同时进入某段代码。",[12,961,962],{},"我们人为规定：",[24,964,967],{"className":965,"code":966,"language":404,"meta":29},[402],"凡是访问 counter\n都必须先获得 counter_mutex\n",[31,968,966],{"__ignoreMap":29},[12,970,971],{},"这样：",[24,973,975],{"className":26,"code":974,"language":28,"meta":29,"style":29},"std::mutex counter_mutex;\n",[31,976,977],{"__ignoreMap":29},[34,978,979,981],{"class":36,"line":37},[34,980,41],{"class":40},[34,982,983],{"class":44},"::mutex counter_mutex;\n",[12,985,986,987,989],{},"才真正起到了保护 ",[31,988,901],{}," 的作用。",[146,991],{},[470,993,995,997],{"id":994},"stdmutex-的基本使用",[31,996,77],{}," 的基本使用",[12,999,1000,1002],{},[31,1001,77],{}," 定义在：",[24,1004,1006],{"className":26,"code":1005,"language":28,"meta":29,"style":29},"#include \u003Cmutex>\n",[31,1007,1008],{"__ignoreMap":29},[34,1009,1010,1012],{"class":36,"line":37},[34,1011,165],{"class":164},[34,1013,1014],{"class":168}," \u003Cmutex>\n",[12,1016,1017],{},"最基础的三个操作是：",[24,1019,1021],{"className":26,"code":1020,"language":28,"meta":29,"style":29},"mutex.lock();\nmutex.try_lock();\nmutex.unlock();\n",[31,1022,1023,1031,1040],{"__ignoreMap":29},[34,1024,1025,1027,1029],{"class":36,"line":37},[34,1026,915],{"class":44},[34,1028,918],{"class":40},[34,1030,353],{"class":44},[34,1032,1033,1035,1038],{"class":36,"line":172},[34,1034,915],{"class":44},[34,1036,1037],{"class":40},"try_lock",[34,1039,353],{"class":44},[34,1041,1042,1044,1046],{"class":36,"line":180},[34,1043,915],{"class":44},[34,1045,933],{"class":40},[34,1047,353],{"class":44},[146,1049],{},[1051,1052,1054,1056],"h4",{"id":1053},"lock获得锁不是加锁是获得锁",[31,1055,83],{},"：获得锁（不是加锁，是获得锁）",[12,1058,1059],{},"最基本的用法：",[24,1061,1063],{"className":26,"code":1062,"language":28,"meta":29,"style":29},"mutex.lock();\n",[31,1064,1065],{"__ignoreMap":29},[34,1066,1067,1069,1071],{"class":36,"line":37},[34,1068,915],{"class":44},[34,1070,918],{"class":40},[34,1072,353],{"class":44},[12,1074,1075],{},"如果当前 mutex 没有被其他线程持有：",[24,1077,1080],{"className":1078,"code":1079,"language":404,"meta":29},[402],"mutex 空闲\n    ↓\n当前线程获得 mutex\n    ↓\n继续执行\n",[31,1081,1079],{"__ignoreMap":29},[12,1083,1084],{},"如果 mutex 已经被其他线程持有：",[24,1086,1089],{"className":1087,"code":1088,"language":404,"meta":29},[402],"线程 A 已经持有 mutex\n          ↓\n线程 B 调用 lock()\n          ↓\n暂时拿不到\n          ↓\n线程 B 阻塞等待\n",[31,1090,1088],{"__ignoreMap":29},[12,1092,1093],{},"等线程 A：",[24,1095,1097],{"className":26,"code":1096,"language":28,"meta":29,"style":29},"mutex.unlock();\n",[31,1098,1099],{"__ignoreMap":29},[34,1100,1101,1103,1105],{"class":36,"line":37},[34,1102,915],{"class":44},[34,1104,933],{"class":40},[34,1106,353],{"class":44},[12,1108,1109],{},"以后，线程 B 才有机会继续。",[12,1111,1112],{},"所以：",[24,1114,1116],{"className":26,"code":1115,"language":28,"meta":29,"style":29},"lock();\n",[31,1117,1118],{"__ignoreMap":29},[34,1119,1120,1122],{"class":36,"line":37},[34,1121,918],{"class":40},[34,1123,353],{"class":44},[12,1125,1126],{},"可以简单记成：",[50,1128,1129],{},[12,1130,1131],{},"拿不到就等。（非常重要）",[146,1133],{},[1051,1135,1137,1139],{"id":1136},"unlock释放锁",[31,1138,90],{},"：释放锁",[12,1141,1142],{},"线程获得 mutex 后：",[24,1144,1145],{"className":26,"code":1062,"language":28,"meta":29,"style":29},[31,1146,1147],{"__ignoreMap":29},[34,1148,1149,1151,1153],{"class":36,"line":37},[34,1150,915],{"class":44},[34,1152,918],{"class":40},[34,1154,353],{"class":44},[12,1156,1157],{},"完成临界区操作以后，就应该：",[24,1159,1160],{"className":26,"code":1096,"language":28,"meta":29,"style":29},[31,1161,1162],{"__ignoreMap":29},[34,1163,1164,1166,1168],{"class":36,"line":37},[34,1165,915],{"class":44},[34,1167,933],{"class":40},[34,1169,353],{"class":44},[12,1171,715],{},[24,1173,1175],{"className":26,"code":1174,"language":28,"meta":29,"style":29},"mutex.lock();\n\n++counter;\n\nmutex.unlock();\n",[31,1176,1177,1185,1189,1195,1199],{"__ignoreMap":29},[34,1178,1179,1181,1183],{"class":36,"line":37},[34,1180,915],{"class":44},[34,1182,918],{"class":40},[34,1184,353],{"class":44},[34,1186,1187],{"class":36,"line":172},[34,1188,184],{"emptyLinePlaceholder":183},[34,1190,1191,1193],{"class":36,"line":180},[34,1192,259],{"class":164},[34,1194,277],{"class":44},[34,1196,1197],{"class":36,"line":187},[34,1198,184],{"emptyLinePlaceholder":183},[34,1200,1201,1203,1205],{"class":36,"line":206},[34,1202,915],{"class":44},[34,1204,933],{"class":40},[34,1206,353],{"class":44},[12,1208,1209],{},"执行过程大致就是：",[24,1211,1214],{"className":1212,"code":1213,"language":404,"meta":29},[402],"获得 mutex（获得不了就阻塞，卡在这里）\n    ↓\n进入临界区\n    ↓\n修改 counter\n    ↓\n离开临界区\n    ↓\n释放 mutex\n",[31,1215,1213],{"__ignoreMap":29},[12,1217,1218],{},"释放以后，其他等待这把 mutex 的线程才有机会继续执行。",[146,1220],{},[1051,1222,1224,1226],{"id":1223},"try_lock拿不到就算了",[31,1225,87],{},"：拿不到就算了",[12,1228,1229],{},"有些时候，我们并不想：",[24,1231,1234],{"className":1232,"code":1233,"language":404,"meta":29},[402],"拿不到锁\n↓\n一直等（阻塞）\n",[31,1235,1233],{"__ignoreMap":29},[12,1237,1238],{},"而是希望：",[24,1240,1243],{"className":1241,"code":1242,"language":404,"meta":29},[402],"拿得到就执行\n拿不到就先干别的（不想阻塞卡在这里）\n",[31,1244,1242],{"__ignoreMap":29},[12,1246,1247],{},"这时可以使用：",[24,1249,1251],{"className":26,"code":1250,"language":28,"meta":29,"style":29},"try_lock()\n",[31,1252,1253],{"__ignoreMap":29},[34,1254,1255,1257],{"class":36,"line":37},[34,1256,1037],{"class":40},[34,1258,220],{"class":44},[12,1260,715],{},[24,1262,1264],{"className":26,"code":1263,"language":28,"meta":29,"style":29},"if (mutex.try_lock())\n{\n    ++counter;\n\n    mutex.unlock();\n}\nelse\n{\n    \u002F\u002F 这一次没有获得 mutex\n}\n",[31,1265,1266,1279,1283,1290,1294,1303,1307,1312,1316,1322],{"__ignoreMap":29},[34,1267,1268,1271,1274,1276],{"class":36,"line":37},[34,1269,1270],{"class":164},"if",[34,1272,1273],{"class":44}," (mutex.",[34,1275,1037],{"class":40},[34,1277,1278],{"class":44},"())\n",[34,1280,1281],{"class":36,"line":172},[34,1282,226],{"class":44},[34,1284,1285,1288],{"class":36,"line":180},[34,1286,1287],{"class":164},"    ++",[34,1289,277],{"class":44},[34,1291,1292],{"class":36,"line":187},[34,1293,184],{"emptyLinePlaceholder":183},[34,1295,1296,1299,1301],{"class":36,"line":206},[34,1297,1298],{"class":44},"    mutex.",[34,1300,933],{"class":40},[34,1302,353],{"class":44},[34,1304,1305],{"class":36,"line":211},[34,1306,289],{"class":44},[34,1308,1309],{"class":36,"line":223},[34,1310,1311],{"class":164},"else\n",[34,1313,1314],{"class":36,"line":229},[34,1315,226],{"class":44},[34,1317,1318],{"class":36,"line":265},[34,1319,1321],{"class":1320},"sJ8bj","    \u002F\u002F 这一次没有获得 mutex\n",[34,1323,1324],{"class":36,"line":271},[34,1325,289],{"class":44},[12,1327,1328],{},"区别可以简单记成：",[24,1330,1333],{"className":1331,"code":1332,"language":404,"meta":29},[402],"lock()\n\n拿不到\n↓\n等待（阻塞）\n",[31,1334,1332],{"__ignoreMap":29},[12,1336,1337],{},"而：",[24,1339,1342],{"className":1340,"code":1341,"language":404,"meta":29},[402],"try_lock()\n\n拿不到\n↓\n立即返回 false（不会阻塞）\n",[31,1343,1341],{"__ignoreMap":29},[12,1345,1346],{},"成功时：",[24,1348,1350],{"className":26,"code":1349,"language":28,"meta":29,"style":29},"true\n",[31,1351,1352],{"__ignoreMap":29},[34,1353,1354],{"class":36,"line":37},[34,1355,1349],{"class":199},[12,1357,1358],{},"失败时：",[24,1360,1362],{"className":26,"code":1361,"language":28,"meta":29,"style":29},"false\n",[31,1363,1364],{"__ignoreMap":29},[34,1365,1366],{"class":36,"line":37},[34,1367,1361],{"class":199},[12,1369,1112],{},[24,1371,1373],{"className":26,"code":1372,"language":28,"meta":29,"style":29},"if (mutex.try_lock())\n{\n    \u002F\u002F 当前线程已经获得锁\n}\n",[31,1374,1375,1385,1389,1394],{"__ignoreMap":29},[34,1376,1377,1379,1381,1383],{"class":36,"line":37},[34,1378,1270],{"class":164},[34,1380,1273],{"class":44},[34,1382,1037],{"class":40},[34,1384,1278],{"class":44},[34,1386,1387],{"class":36,"line":172},[34,1388,226],{"class":44},[34,1390,1391],{"class":36,"line":180},[34,1392,1393],{"class":1320},"    \u002F\u002F 当前线程已经获得锁\n",[34,1395,1396],{"class":36,"line":187},[34,1397,289],{"class":44},[12,1399,1400,1401,1403],{},"需要注意，标准允许 ",[31,1402,87],{}," 偶发性失败。",[12,1405,1406],{},"所以不要把：",[24,1408,1410],{"className":26,"code":1409,"language":28,"meta":29,"style":29},"try_lock() == false\n",[31,1411,1412],{"__ignoreMap":29},[34,1413,1414,1416,1419,1422],{"class":36,"line":37},[34,1415,1037],{"class":40},[34,1417,1418],{"class":44},"() ",[34,1420,1421],{"class":164},"==",[34,1423,1424],{"class":199}," false\n",[12,1426,1427],{},"绝对理解成：",[50,1429,1430],{},[12,1431,1432],{},"现在肯定有另一个线程持有 mutex。",[12,1434,1435],{},"更简单的理解就是：",[50,1437,1438],{},[12,1439,1440],{},"这次没有获得锁。",[146,1442],{},[470,1444,1446,1447],{"id":1445},"用-mutex-修复-counter","用 mutex 修复 ",[31,1448,901],{},[12,1450,1451],{},"之前的数据竞争可以这样解决：",[24,1453,1455],{"className":26,"code":1454,"language":28,"meta":29,"style":29},"#include \u003Cprint>\n#include \u003Cmutex>\n#include \u003Cthread>\n\nint counter = 0;\nstd::mutex counter_mutex;\n\nvoid add_many()\n{\n    for (int i = 0; i \u003C 100000; ++i)\n    {\n        counter_mutex.lock();\n\n        ++counter;\n\n        counter_mutex.unlock();\n    }\n}\n\nint main()\n{\n    std::thread t1(add_many);\n    std::thread t2(add_many);\n\n    t1.join();\n    t2.join();\n\n    std::println(\"{}\", counter);\n}\n",[31,1456,1457,1463,1469,1475,1479,1491,1497,1501,1509,1513,1539,1543,1552,1556,1562,1566,1574,1578,1582,1586,1594,1598,1608,1618,1623,1632,1641,1646,1661],{"__ignoreMap":29},[34,1458,1459,1461],{"class":36,"line":37},[34,1460,165],{"class":164},[34,1462,169],{"class":168},[34,1464,1465,1467],{"class":36,"line":172},[34,1466,165],{"class":164},[34,1468,1014],{"class":168},[34,1470,1471,1473],{"class":36,"line":180},[34,1472,165],{"class":164},[34,1474,177],{"class":168},[34,1476,1477],{"class":36,"line":187},[34,1478,184],{"emptyLinePlaceholder":183},[34,1480,1481,1483,1485,1487,1489],{"class":36,"line":206},[34,1482,190],{"class":164},[34,1484,193],{"class":44},[34,1486,196],{"class":164},[34,1488,200],{"class":199},[34,1490,203],{"class":44},[34,1492,1493,1495],{"class":36,"line":211},[34,1494,41],{"class":40},[34,1496,983],{"class":44},[34,1498,1499],{"class":36,"line":223},[34,1500,184],{"emptyLinePlaceholder":183},[34,1502,1503,1505,1507],{"class":36,"line":229},[34,1504,214],{"class":164},[34,1506,217],{"class":40},[34,1508,220],{"class":44},[34,1510,1511],{"class":36,"line":265},[34,1512,226],{"class":44},[34,1514,1515,1517,1519,1521,1523,1525,1527,1529,1531,1533,1535,1537],{"class":36,"line":271},[34,1516,232],{"class":164},[34,1518,235],{"class":44},[34,1520,190],{"class":164},[34,1522,240],{"class":44},[34,1524,196],{"class":164},[34,1526,200],{"class":199},[34,1528,247],{"class":44},[34,1530,250],{"class":164},[34,1532,253],{"class":199},[34,1534,256],{"class":44},[34,1536,259],{"class":164},[34,1538,262],{"class":44},[34,1540,1541],{"class":36,"line":280},[34,1542,268],{"class":44},[34,1544,1545,1548,1550],{"class":36,"line":286},[34,1546,1547],{"class":44},"        counter_mutex.",[34,1549,918],{"class":40},[34,1551,353],{"class":44},[34,1553,1554],{"class":36,"line":292},[34,1555,184],{"emptyLinePlaceholder":183},[34,1557,1558,1560],{"class":36,"line":297},[34,1559,274],{"class":164},[34,1561,277],{"class":44},[34,1563,1564],{"class":36,"line":307},[34,1565,184],{"emptyLinePlaceholder":183},[34,1567,1568,1570,1572],{"class":36,"line":312},[34,1569,1547],{"class":44},[34,1571,933],{"class":40},[34,1573,353],{"class":44},[34,1575,1576],{"class":36,"line":327},[34,1577,283],{"class":44},[34,1579,1580],{"class":36,"line":339},[34,1581,289],{"class":44},[34,1583,1584],{"class":36,"line":344},[34,1585,184],{"emptyLinePlaceholder":183},[34,1587,1588,1590,1592],{"class":36,"line":356},[34,1589,190],{"class":164},[34,1591,302],{"class":40},[34,1593,220],{"class":44},[34,1595,1596],{"class":36,"line":366},[34,1597,226],{"class":44},[34,1599,1600,1602,1604,1606],{"class":36,"line":371},[34,1601,315],{"class":40},[34,1603,318],{"class":44},[34,1605,321],{"class":40},[34,1607,324],{"class":44},[34,1609,1610,1612,1614,1616],{"class":36,"line":391},[34,1611,315],{"class":40},[34,1613,318],{"class":44},[34,1615,334],{"class":40},[34,1617,324],{"class":44},[34,1619,1621],{"class":36,"line":1620},24,[34,1622,184],{"emptyLinePlaceholder":183},[34,1624,1626,1628,1630],{"class":36,"line":1625},25,[34,1627,347],{"class":44},[34,1629,350],{"class":40},[34,1631,353],{"class":44},[34,1633,1635,1637,1639],{"class":36,"line":1634},26,[34,1636,359],{"class":44},[34,1638,350],{"class":40},[34,1640,353],{"class":44},[34,1642,1644],{"class":36,"line":1643},27,[34,1645,184],{"emptyLinePlaceholder":183},[34,1647,1649,1651,1653,1655,1657,1659],{"class":36,"line":1648},28,[34,1650,315],{"class":40},[34,1652,376],{"class":44},[34,1654,379],{"class":40},[34,1656,382],{"class":44},[34,1658,385],{"class":168},[34,1660,388],{"class":44},[34,1662,1664],{"class":36,"line":1663},29,[34,1665,289],{"class":44},[12,1667,1668],{},[16,1669,1670],{},"运行结果：",[24,1672,1674],{"className":1673,"code":448,"language":404,"meta":29},[402],[31,1675,448],{"__ignoreMap":29},[12,1677,1678],{},[1679,1680],"img",{"alt":29,"src":1681},"https:\u002F\u002Fcdn.tungchiahui.cn\u002Ftungwebsite\u002Fassets\u002Fimages\u002F2023\u002F10\u002F05\u002F1789278766269-efd65a5f.webp",[12,1683,1684,1685,1688],{},"如上图，只有第一个有mutex机制的正确输出了",[31,1686,1687],{},"200000","，而其他没mutex机制的输出是不固定的。",[12,1690,1691],{},"现在所有想修改：",[24,1693,1694],{"className":26,"code":681,"language":28,"meta":29,"style":29},[31,1695,1696],{"__ignoreMap":29},[34,1697,1698],{"class":36,"line":37},[34,1699,681],{"class":44},[12,1701,1702],{},"的线程，都必须先：",[24,1704,1706],{"className":26,"code":1705,"language":28,"meta":29,"style":29},"counter_mutex.lock();\n",[31,1707,1708],{"__ignoreMap":29},[34,1709,1710,1713,1715],{"class":36,"line":37},[34,1711,1712],{"class":44},"counter_mutex.",[34,1714,918],{"class":40},[34,1716,353],{"class":44},[12,1718,1719],{},"于是可能出现：",[24,1721,1724],{"className":1722,"code":1723,"language":404,"meta":29},[402],"线程 A 获得 counter_mutex\n        ↓\n    ++counter\n        ↓\n线程 A 释放 counter_mutex\n        ↓\n线程 B 获得 counter_mutex\n        ↓\n    ++counter\n",[31,1725,1723],{"__ignoreMap":29},[12,1727,1728],{},"而不会两个线程同时修改。",[12,1730,1731],{},"但是这种写法还有一个很大的问题：",[24,1733,1735],{"className":26,"code":1734,"language":28,"meta":29,"style":29},"lock();\n...\nunlock();\n",[31,1736,1737,1743,1748],{"__ignoreMap":29},[34,1738,1739,1741],{"class":36,"line":37},[34,1740,918],{"class":40},[34,1742,353],{"class":44},[34,1744,1745],{"class":36,"line":172},[34,1746,1747],{"class":44},"...\n",[34,1749,1750,1752],{"class":36,"line":180},[34,1751,933],{"class":40},[34,1753,353],{"class":44},[12,1755,1756],{},"完全由程序员自己管理。",[146,1758],{},[470,1760,1762,1763,97,1765],{"id":1761},"为什么不推荐手写-lock-unlock","为什么不推荐手写 ",[31,1764,83],{},[31,1766,90],{},[12,1768,1769],{},"下面这段代码看起来没有什么问题：",[24,1771,1773],{"className":26,"code":1772,"language":28,"meta":29,"style":29},"mutex.lock();\n\nchange_shared_state();\n\nmutex.unlock();\n",[31,1774,1775,1783,1787,1794,1798],{"__ignoreMap":29},[34,1776,1777,1779,1781],{"class":36,"line":37},[34,1778,915],{"class":44},[34,1780,918],{"class":40},[34,1782,353],{"class":44},[34,1784,1785],{"class":36,"line":172},[34,1786,184],{"emptyLinePlaceholder":183},[34,1788,1789,1792],{"class":36,"line":180},[34,1790,1791],{"class":40},"change_shared_state",[34,1793,353],{"class":44},[34,1795,1796],{"class":36,"line":187},[34,1797,184],{"emptyLinePlaceholder":183},[34,1799,1800,1802,1804],{"class":36,"line":206},[34,1801,915],{"class":44},[34,1803,933],{"class":40},[34,1805,353],{"class":44},[12,1807,1808],{},"但以后代码可能变成：",[24,1810,1812],{"className":26,"code":1811,"language":28,"meta":29,"style":29},"mutex.lock();\n\nif (something_wrong())\n{\n    return;\n}\n\nchange_shared_state();\n\nmutex.unlock();\n",[31,1813,1814,1822,1826,1837,1841,1848,1852,1856,1862,1866],{"__ignoreMap":29},[34,1815,1816,1818,1820],{"class":36,"line":37},[34,1817,915],{"class":44},[34,1819,918],{"class":40},[34,1821,353],{"class":44},[34,1823,1824],{"class":36,"line":172},[34,1825,184],{"emptyLinePlaceholder":183},[34,1827,1828,1830,1832,1835],{"class":36,"line":180},[34,1829,1270],{"class":164},[34,1831,235],{"class":44},[34,1833,1834],{"class":40},"something_wrong",[34,1836,1278],{"class":44},[34,1838,1839],{"class":36,"line":187},[34,1840,226],{"class":44},[34,1842,1843,1846],{"class":36,"line":206},[34,1844,1845],{"class":164},"    return",[34,1847,203],{"class":44},[34,1849,1850],{"class":36,"line":211},[34,1851,289],{"class":44},[34,1853,1854],{"class":36,"line":223},[34,1855,184],{"emptyLinePlaceholder":183},[34,1857,1858,1860],{"class":36,"line":229},[34,1859,1791],{"class":40},[34,1861,353],{"class":44},[34,1863,1864],{"class":36,"line":265},[34,1865,184],{"emptyLinePlaceholder":183},[34,1867,1868,1870,1872],{"class":36,"line":271},[34,1869,915],{"class":44},[34,1871,933],{"class":40},[34,1873,353],{"class":44},[12,1875,578],{},[24,1877,1879],{"className":26,"code":1878,"language":28,"meta":29,"style":29},"something_wrong()\n",[31,1880,1881],{"__ignoreMap":29},[34,1882,1883,1885],{"class":36,"line":37},[34,1884,1834],{"class":40},[34,1886,220],{"class":44},[12,1888,1889],{},"为真：",[24,1891,1893],{"className":26,"code":1892,"language":28,"meta":29,"style":29},"return;\n",[31,1894,1895],{"__ignoreMap":29},[34,1896,1897,1900],{"class":36,"line":37},[34,1898,1899],{"class":164},"return",[34,1901,203],{"class":44},[12,1903,1904],{},"就会直接离开函数。",[12,1906,1907],{},"于是：",[24,1909,1910],{"className":26,"code":1096,"language":28,"meta":29,"style":29},[31,1911,1912],{"__ignoreMap":29},[34,1913,1914,1916,1918],{"class":36,"line":37},[34,1915,915],{"class":44},[34,1917,933],{"class":40},[34,1919,353],{"class":44},[12,1921,1922],{},"永远不会执行。",[12,1924,1925],{},"mutex 会一直保持锁定状态。",[12,1927,1928],{},"其他线程如果再：",[24,1930,1931],{"className":26,"code":1062,"language":28,"meta":29,"style":29},[31,1932,1933],{"__ignoreMap":29},[34,1934,1935,1937,1939],{"class":36,"line":37},[34,1936,915],{"class":44},[34,1938,918],{"class":40},[34,1940,353],{"class":44},[12,1942,1943],{},"就可能一直卡在那里。",[12,1945,1946],{},"还有异常：",[24,1948,1950],{"className":26,"code":1949,"language":28,"meta":29,"style":29},"mutex.lock();\n\nsome_function();\n\nmutex.unlock();\n",[31,1951,1952,1960,1964,1971,1975],{"__ignoreMap":29},[34,1953,1954,1956,1958],{"class":36,"line":37},[34,1955,915],{"class":44},[34,1957,918],{"class":40},[34,1959,353],{"class":44},[34,1961,1962],{"class":36,"line":172},[34,1963,184],{"emptyLinePlaceholder":183},[34,1965,1966,1969],{"class":36,"line":180},[34,1967,1968],{"class":40},"some_function",[34,1970,353],{"class":44},[34,1972,1973],{"class":36,"line":187},[34,1974,184],{"emptyLinePlaceholder":183},[34,1976,1977,1979,1981],{"class":36,"line":206},[34,1978,915],{"class":44},[34,1980,933],{"class":40},[34,1982,353],{"class":44},[12,1984,578],{},[24,1986,1988],{"className":26,"code":1987,"language":28,"meta":29,"style":29},"some_function();\n",[31,1989,1990],{"__ignoreMap":29},[34,1991,1992,1994],{"class":36,"line":37},[34,1993,1968],{"class":40},[34,1995,353],{"class":44},[12,1997,1998],{},"抛出异常，后面的：",[24,2000,2001],{"className":26,"code":1096,"language":28,"meta":29,"style":29},[31,2002,2003],{"__ignoreMap":29},[34,2004,2005,2007,2009],{"class":36,"line":37},[34,2006,915],{"class":44},[34,2008,933],{"class":40},[34,2010,353],{"class":44},[12,2012,2013],{},"同样不会正常执行。",[12,2015,2016],{},"因此手动管理：",[24,2018,2020],{"className":26,"code":2019,"language":28,"meta":29,"style":29},"lock()\nunlock()\n",[31,2021,2022,2028],{"__ignoreMap":29},[34,2023,2024,2026],{"class":36,"line":37},[34,2025,918],{"class":40},[34,2027,220],{"class":44},[34,2029,2030,2032],{"class":36,"line":172},[34,2031,933],{"class":40},[34,2033,220],{"class":44},[12,2035,2036],{},"最大的问题就是：",[50,2038,2039],{},[12,2040,2041],{},"必须保证程序的每一条离开路径都正确释放锁。",[12,2043,2044],{},"代码一复杂，就很容易遗漏。",[12,2046,2047,2048,2051,2052,2055],{},"这很像内存管理里的",[31,2049,2050],{},"new","和",[31,2053,2054],{},"delete","所面临的问题，在内存管理中，我们使用的是RAII思路去解决这个问题，也就是智能指针。",[12,2057,2058],{},"在这里，也正是 RAII 特别适合解决的问题。",[146,2060],{},[149,2062,2064],{"id":2063},"raii-锁管理","RAII 锁管理",[470,2066,2068],{"id":2067},"raii-为什么适合管理锁","RAII 为什么适合管理锁",[12,2070,2071],{},"RAII 的核心思想之前已经学过：",[50,2073,2074],{},[12,2075,2076],{},"让资源的生命周期由对象生命周期管理。",[12,2078,2079],{},"mutex 的所有权也可以看成一种资源。",[12,2081,2082],{},"于是我们希望有这样一个对象：",[24,2084,2087],{"className":2085,"code":2086,"language":404,"meta":29},[402],"对象构造\n↓\nmutex.lock()\n\n对象析构\n↓\nmutex.unlock()\n",[31,2088,2086],{"__ignoreMap":29},[12,2090,2091],{},"这样只需要：",[24,2093,2095],{"className":26,"code":2094,"language":28,"meta":29,"style":29},"{\n    某个 RAII 锁对象 lock(mutex);\n\n    \u002F\u002F 临界区\n}\n",[31,2096,2097,2101,2111,2115,2120],{"__ignoreMap":29},[34,2098,2099],{"class":36,"line":37},[34,2100,226],{"class":44},[34,2102,2103,2106,2108],{"class":36,"line":172},[34,2104,2105],{"class":44},"    某个 RAII 锁对象 ",[34,2107,918],{"class":40},[34,2109,2110],{"class":44},"(mutex);\n",[34,2112,2113],{"class":36,"line":180},[34,2114,184],{"emptyLinePlaceholder":183},[34,2116,2117],{"class":36,"line":187},[34,2118,2119],{"class":1320},"    \u002F\u002F 临界区\n",[34,2121,2122],{"class":36,"line":206},[34,2123,289],{"class":44},[12,2125,2126],{},"当作用域结束：",[24,2128,2131],{"className":2129,"code":2130,"language":404,"meta":29},[402],"}\n↓\nlock 对象自动析构\n↓\n自动释放 mutex\n",[31,2132,2130],{"__ignoreMap":29},[12,2134,2135],{},"就不需要程序员自己到处写：",[24,2137,2139],{"className":26,"code":2138,"language":28,"meta":29,"style":29},"unlock();\n",[31,2140,2141],{"__ignoreMap":29},[34,2142,2143,2145],{"class":36,"line":37},[34,2144,933],{"class":40},[34,2146,353],{"class":44},[12,2148,2149],{},"这就是：",[50,2151,2152],{},[12,2153,2154],{},"RAII 锁",[146,2156],{},[470,2158,2160],{"id":2159},"stdlock_guard",[31,2161,107],{},[12,2163,2164],{},"最简单、最常用的 RAII 锁之一就是：",[24,2166,2168],{"className":26,"code":2167,"language":28,"meta":29,"style":29},"std::lock_guard\n",[31,2169,2170],{"__ignoreMap":29},[34,2171,2172,2174],{"class":36,"line":37},[34,2173,41],{"class":40},[34,2175,2176],{"class":44},"::lock_guard\n",[12,2178,715],{},[24,2180,2182],{"className":26,"code":2181,"language":28,"meta":29,"style":29},"std::mutex mutex;\n\nvoid foo()\n{\n    std::lock_guard\u003Cstd::mutex> lock(mutex);\n\n    \u002F\u002F 临界区\n}\n",[31,2183,2184,2190,2194,2203,2207,2229,2233,2237],{"__ignoreMap":29},[34,2185,2186,2188],{"class":36,"line":37},[34,2187,41],{"class":40},[34,2189,881],{"class":44},[34,2191,2192],{"class":36,"line":172},[34,2193,184],{"emptyLinePlaceholder":183},[34,2195,2196,2198,2201],{"class":36,"line":180},[34,2197,214],{"class":164},[34,2199,2200],{"class":40}," foo",[34,2202,220],{"class":44},[34,2204,2205],{"class":36,"line":187},[34,2206,226],{"class":44},[34,2208,2209,2211,2214,2216,2218,2221,2224,2227],{"class":36,"line":206},[34,2210,315],{"class":40},[34,2212,2213],{"class":44},"::lock_guard",[34,2215,250],{"class":164},[34,2217,41],{"class":40},[34,2219,2220],{"class":44},"::mutex",[34,2222,2223],{"class":164},">",[34,2225,2226],{"class":40}," lock",[34,2228,2110],{"class":44},[34,2230,2231],{"class":36,"line":211},[34,2232,184],{"emptyLinePlaceholder":183},[34,2234,2235],{"class":36,"line":223},[34,2236,2119],{"class":1320},[34,2238,2239],{"class":36,"line":229},[34,2240,289],{"class":44},[12,2242,2243],{},"执行：",[24,2245,2247],{"className":26,"code":2246,"language":28,"meta":29,"style":29},"std::lock_guard\u003Cstd::mutex> lock(mutex);\n",[31,2248,2249],{"__ignoreMap":29},[34,2250,2251,2253,2255,2258,2260,2262,2264,2267,2270,2272,2274,2276],{"class":36,"line":37},[34,2252,41],{"class":40},[34,2254,376],{"class":44},[34,2256,2257],{"class":40},"lock_guard",[34,2259,250],{"class":44},[34,2261,41],{"class":40},[34,2263,376],{"class":44},[34,2265,2266],{"class":40},"mutex",[34,2268,2269],{"class":44},"> ",[34,2271,918],{"class":40},[34,2273,382],{"class":44},[34,2275,2266],{"class":40},[34,2277,2278],{"class":44},");\n",[12,2280,2281],{},"时，可以简单理解成：",[24,2283,2286],{"className":2284,"code":2285,"language":404,"meta":29},[402],"lock_guard 构造\n        ↓\nmutex.lock()\n",[31,2287,2285],{"__ignoreMap":29},[12,2289,2290],{},"离开作用域时：",[24,2292,2295],{"className":2293,"code":2294,"language":404,"meta":29},[402],"lock_guard 析构\n        ↓\nmutex.unlock()\n",[31,2296,2294],{"__ignoreMap":29},[12,2298,1112],{},[24,2300,2302],{"className":26,"code":2301,"language":28,"meta":29,"style":29},"void foo()\n{\n    std::lock_guard\u003Cstd::mutex> lock(mutex);\n\n    change_shared_state();\n\n} \u002F\u002F 自动释放 mutex\n",[31,2303,2304,2312,2316,2334,2338,2345,2349],{"__ignoreMap":29},[34,2305,2306,2308,2310],{"class":36,"line":37},[34,2307,214],{"class":164},[34,2309,2200],{"class":40},[34,2311,220],{"class":44},[34,2313,2314],{"class":36,"line":172},[34,2315,226],{"class":44},[34,2317,2318,2320,2322,2324,2326,2328,2330,2332],{"class":36,"line":180},[34,2319,315],{"class":40},[34,2321,2213],{"class":44},[34,2323,250],{"class":164},[34,2325,41],{"class":40},[34,2327,2220],{"class":44},[34,2329,2223],{"class":164},[34,2331,2226],{"class":40},[34,2333,2110],{"class":44},[34,2335,2336],{"class":36,"line":187},[34,2337,184],{"emptyLinePlaceholder":183},[34,2339,2340,2343],{"class":36,"line":206},[34,2341,2342],{"class":40},"    change_shared_state",[34,2344,353],{"class":44},[34,2346,2347],{"class":36,"line":211},[34,2348,184],{"emptyLinePlaceholder":183},[34,2350,2351,2354],{"class":36,"line":223},[34,2352,2353],{"class":44},"}",[34,2355,2356],{"class":1320}," \u002F\u002F 自动释放 mutex\n",[12,2358,2359],{},"不需要再写：",[24,2361,2362],{"className":26,"code":1096,"language":28,"meta":29,"style":29},[31,2363,2364],{"__ignoreMap":29},[34,2365,2366,2368,2370],{"class":36,"line":37},[34,2367,915],{"class":44},[34,2369,933],{"class":40},[34,2371,353],{"class":44},[146,2373],{},[470,2375,2377,2378,2380],{"id":2376},"用-lock_guard-改写-counter","用 ",[31,2379,2257],{}," 改写 counter",[12,2382,2383],{},"之前的代码可以写成：",[24,2385,2387],{"className":26,"code":2386,"language":28,"meta":29,"style":29},"#include \u003Cprint>\n#include \u003Cmutex>\n#include \u003Cthread>\n\nint counter = 0;\nstd::mutex counter_mutex;\n\nvoid add_many()\n{\n    for (int i = 0; i \u003C 100000; ++i)\n    {\n        std::lock_guard\u003Cstd::mutex> lock(counter_mutex);\n\n        ++counter;\n    }\n}\n\nint main()\n{\n    std::thread t1(add_many);\n    std::thread t2(add_many);\n\n    t1.join();\n    t2.join();\n\n    std::println(\"{}\", counter);\n}\n",[31,2388,2389,2395,2401,2407,2411,2423,2429,2433,2441,2445,2471,2475,2495,2499,2505,2509,2513,2517,2525,2529,2539,2549,2553,2561,2569,2573,2587],{"__ignoreMap":29},[34,2390,2391,2393],{"class":36,"line":37},[34,2392,165],{"class":164},[34,2394,169],{"class":168},[34,2396,2397,2399],{"class":36,"line":172},[34,2398,165],{"class":164},[34,2400,1014],{"class":168},[34,2402,2403,2405],{"class":36,"line":180},[34,2404,165],{"class":164},[34,2406,177],{"class":168},[34,2408,2409],{"class":36,"line":187},[34,2410,184],{"emptyLinePlaceholder":183},[34,2412,2413,2415,2417,2419,2421],{"class":36,"line":206},[34,2414,190],{"class":164},[34,2416,193],{"class":44},[34,2418,196],{"class":164},[34,2420,200],{"class":199},[34,2422,203],{"class":44},[34,2424,2425,2427],{"class":36,"line":211},[34,2426,41],{"class":40},[34,2428,983],{"class":44},[34,2430,2431],{"class":36,"line":223},[34,2432,184],{"emptyLinePlaceholder":183},[34,2434,2435,2437,2439],{"class":36,"line":229},[34,2436,214],{"class":164},[34,2438,217],{"class":40},[34,2440,220],{"class":44},[34,2442,2443],{"class":36,"line":265},[34,2444,226],{"class":44},[34,2446,2447,2449,2451,2453,2455,2457,2459,2461,2463,2465,2467,2469],{"class":36,"line":271},[34,2448,232],{"class":164},[34,2450,235],{"class":44},[34,2452,190],{"class":164},[34,2454,240],{"class":44},[34,2456,196],{"class":164},[34,2458,200],{"class":199},[34,2460,247],{"class":44},[34,2462,250],{"class":164},[34,2464,253],{"class":199},[34,2466,256],{"class":44},[34,2468,259],{"class":164},[34,2470,262],{"class":44},[34,2472,2473],{"class":36,"line":280},[34,2474,268],{"class":44},[34,2476,2477,2480,2482,2484,2486,2488,2490,2492],{"class":36,"line":286},[34,2478,2479],{"class":40},"        std",[34,2481,2213],{"class":44},[34,2483,250],{"class":164},[34,2485,41],{"class":40},[34,2487,2220],{"class":44},[34,2489,2223],{"class":164},[34,2491,2226],{"class":40},[34,2493,2494],{"class":44},"(counter_mutex);\n",[34,2496,2497],{"class":36,"line":292},[34,2498,184],{"emptyLinePlaceholder":183},[34,2500,2501,2503],{"class":36,"line":297},[34,2502,274],{"class":164},[34,2504,277],{"class":44},[34,2506,2507],{"class":36,"line":307},[34,2508,283],{"class":44},[34,2510,2511],{"class":36,"line":312},[34,2512,289],{"class":44},[34,2514,2515],{"class":36,"line":327},[34,2516,184],{"emptyLinePlaceholder":183},[34,2518,2519,2521,2523],{"class":36,"line":339},[34,2520,190],{"class":164},[34,2522,302],{"class":40},[34,2524,220],{"class":44},[34,2526,2527],{"class":36,"line":344},[34,2528,226],{"class":44},[34,2530,2531,2533,2535,2537],{"class":36,"line":356},[34,2532,315],{"class":40},[34,2534,318],{"class":44},[34,2536,321],{"class":40},[34,2538,324],{"class":44},[34,2540,2541,2543,2545,2547],{"class":36,"line":366},[34,2542,315],{"class":40},[34,2544,318],{"class":44},[34,2546,334],{"class":40},[34,2548,324],{"class":44},[34,2550,2551],{"class":36,"line":371},[34,2552,184],{"emptyLinePlaceholder":183},[34,2554,2555,2557,2559],{"class":36,"line":391},[34,2556,347],{"class":44},[34,2558,350],{"class":40},[34,2560,353],{"class":44},[34,2562,2563,2565,2567],{"class":36,"line":1620},[34,2564,359],{"class":44},[34,2566,350],{"class":40},[34,2568,353],{"class":44},[34,2570,2571],{"class":36,"line":1625},[34,2572,184],{"emptyLinePlaceholder":183},[34,2574,2575,2577,2579,2581,2583,2585],{"class":36,"line":1634},[34,2576,315],{"class":40},[34,2578,376],{"class":44},[34,2580,379],{"class":40},[34,2582,382],{"class":44},[34,2584,385],{"class":168},[34,2586,388],{"class":44},[34,2588,2589],{"class":36,"line":1643},[34,2590,289],{"class":44},[12,2592,2593],{},[16,2594,1670],{},[24,2596,2598],{"className":2597,"code":448,"language":404,"meta":29},[402],[31,2599,448],{"__ignoreMap":29},[12,2601,2602],{},"每轮循环：",[24,2604,2607],{"className":2605,"code":2606,"language":404,"meta":29},[402],"创建 lock_guard\n      ↓\n获得 counter_mutex\n      ↓\n++counter\n      ↓\n这一轮作用域结束\n      ↓\nlock_guard 析构\n      ↓\n自动释放 mutex\n",[31,2608,2606],{"__ignoreMap":29},[12,2610,2611],{},"这样就算以后代码中出现：",[24,2613,2614],{"className":26,"code":1892,"language":28,"meta":29,"style":29},[31,2615,2616],{"__ignoreMap":29},[34,2617,2618,2620],{"class":36,"line":37},[34,2619,1899],{"class":164},[34,2621,203],{"class":44},[12,2623,2624],{},"或者正常的异常栈展开，也不容易忘记释放锁。",[12,2626,2627],{},"C++17 开始还可以利用类模板参数推导写成：",[24,2629,2631],{"className":26,"code":2630,"language":28,"meta":29,"style":29},"std::lock_guard lock(counter_mutex);\n",[31,2632,2633],{"__ignoreMap":29},[34,2634,2635,2637,2639,2641,2643,2645,2648],{"class":36,"line":37},[34,2636,41],{"class":40},[34,2638,376],{"class":44},[34,2640,2257],{"class":40},[34,2642,2226],{"class":40},[34,2644,382],{"class":44},[34,2646,2647],{"class":40},"counter_mutex",[34,2649,2278],{"class":44},[12,2651,2652],{},"不一定必须写：",[24,2654,2656],{"className":26,"code":2655,"language":28,"meta":29,"style":29},"std::lock_guard\u003Cstd::mutex>\n",[31,2657,2658],{"__ignoreMap":29},[34,2659,2660,2662,2664,2666,2668,2670],{"class":36,"line":37},[34,2661,41],{"class":40},[34,2663,2213],{"class":44},[34,2665,250],{"class":164},[34,2667,41],{"class":40},[34,2669,2220],{"class":44},[34,2671,2672],{"class":164},">\n",[146,2674],{},[470,2676,2678,2679,2682],{"id":2677},"可以用-主动缩小锁的作用域","可以用 ",[31,2680,2681],{},"{}"," 主动缩小锁的作用域",[12,2684,2685],{},"RAII 锁什么时候释放？",[12,2687,2688],{},"答案就是：",[50,2690,2691],{},[12,2692,2693],{},"锁对象什么时候析构，mutex 就什么时候释放。",[12,2695,2696,2697,2699],{},"所以我们可以使用 ",[31,2698,2681],{}," 创建一个局部作用域：",[24,2701,2703],{"className":26,"code":2702,"language":28,"meta":29,"style":29},"void foo()\n{\n    do_something();\n\n    {\n        std::lock_guard lock(mutex);\n\n        change_shared_state();\n    }\n\n    do_other_work();\n}\n",[31,2704,2705,2713,2717,2724,2728,2732,2743,2747,2754,2758,2762,2769],{"__ignoreMap":29},[34,2706,2707,2709,2711],{"class":36,"line":37},[34,2708,214],{"class":164},[34,2710,2200],{"class":40},[34,2712,220],{"class":44},[34,2714,2715],{"class":36,"line":172},[34,2716,226],{"class":44},[34,2718,2719,2722],{"class":36,"line":180},[34,2720,2721],{"class":40},"    do_something",[34,2723,353],{"class":44},[34,2725,2726],{"class":36,"line":187},[34,2727,184],{"emptyLinePlaceholder":183},[34,2729,2730],{"class":36,"line":206},[34,2731,268],{"class":44},[34,2733,2734,2736,2739,2741],{"class":36,"line":211},[34,2735,2479],{"class":40},[34,2737,2738],{"class":44},"::lock_guard ",[34,2740,918],{"class":40},[34,2742,2110],{"class":44},[34,2744,2745],{"class":36,"line":223},[34,2746,184],{"emptyLinePlaceholder":183},[34,2748,2749,2752],{"class":36,"line":229},[34,2750,2751],{"class":40},"        change_shared_state",[34,2753,353],{"class":44},[34,2755,2756],{"class":36,"line":265},[34,2757,283],{"class":44},[34,2759,2760],{"class":36,"line":271},[34,2761,184],{"emptyLinePlaceholder":183},[34,2763,2764,2767],{"class":36,"line":280},[34,2765,2766],{"class":40},"    do_other_work",[34,2768,353],{"class":44},[34,2770,2771],{"class":36,"line":286},[34,2772,289],{"class":44},[12,2774,2775],{},"执行逻辑：",[24,2777,2780],{"className":2778,"code":2779,"language":404,"meta":29},[402],"do_something()\n\n获得 mutex\n↓\n修改共享数据\n↓\n释放 mutex\n\ndo_other_work()\n",[31,2781,2779],{"__ignoreMap":29},[12,2783,971],{},[24,2785,2787],{"className":26,"code":2786,"language":28,"meta":29,"style":29},"do_other_work();\n",[31,2788,2789],{"__ignoreMap":29},[34,2790,2791,2794],{"class":36,"line":37},[34,2792,2793],{"class":40},"do_other_work",[34,2795,353],{"class":44},[12,2797,2798],{},"就不会继续占着 mutex。",[146,2800],{},[470,2802,2803],{"id":2803},"临界区应该尽量小",[12,2805,2806],{},"假设写成：",[24,2808,2810],{"className":26,"code":2809,"language":28,"meta":29,"style":29},"std::lock_guard lock(mutex);\n\ndo_big_calculation();\nsave_file();\nsend_network_data();\n\nupdate_shared_state();\n",[31,2811,2812,2828,2832,2839,2846,2853,2857],{"__ignoreMap":29},[34,2813,2814,2816,2818,2820,2822,2824,2826],{"class":36,"line":37},[34,2815,41],{"class":40},[34,2817,376],{"class":44},[34,2819,2257],{"class":40},[34,2821,2226],{"class":40},[34,2823,382],{"class":44},[34,2825,2266],{"class":40},[34,2827,2278],{"class":44},[34,2829,2830],{"class":36,"line":172},[34,2831,184],{"emptyLinePlaceholder":183},[34,2833,2834,2837],{"class":36,"line":180},[34,2835,2836],{"class":40},"do_big_calculation",[34,2838,353],{"class":44},[34,2840,2841,2844],{"class":36,"line":187},[34,2842,2843],{"class":40},"save_file",[34,2845,353],{"class":44},[34,2847,2848,2851],{"class":36,"line":206},[34,2849,2850],{"class":40},"send_network_data",[34,2852,353],{"class":44},[34,2854,2855],{"class":36,"line":211},[34,2856,184],{"emptyLinePlaceholder":183},[34,2858,2859,2862],{"class":36,"line":223},[34,2860,2861],{"class":40},"update_shared_state",[34,2863,353],{"class":44},[12,2865,2866],{},"那么整个过程中：",[24,2868,2871],{"className":2869,"code":2870,"language":404,"meta":29},[402],"mutex 一直被当前线程持有\n",[31,2872,2870],{"__ignoreMap":29},[12,2874,2875],{},"其他需要这把 mutex 的线程只能：",[24,2877,2880],{"className":2878,"code":2879,"language":404,"meta":29},[402],"等待\n",[31,2881,2879],{"__ignoreMap":29},[12,2883,2884],{},"但：",[24,2886,2888],{"className":26,"code":2887,"language":28,"meta":29,"style":29},"do_big_calculation();\nsave_file();\nsend_network_data();\n",[31,2889,2890,2896,2902],{"__ignoreMap":29},[34,2891,2892,2894],{"class":36,"line":37},[34,2893,2836],{"class":40},[34,2895,353],{"class":44},[34,2897,2898,2900],{"class":36,"line":172},[34,2899,2843],{"class":40},[34,2901,353],{"class":44},[34,2903,2904,2906],{"class":36,"line":180},[34,2905,2850],{"class":40},[34,2907,353],{"class":44},[12,2909,2910],{},"可能根本不需要访问共享数据。",[12,2912,2913],{},"更合理的方式通常是：",[24,2915,2917],{"className":26,"code":2916,"language":28,"meta":29,"style":29},"do_big_calculation();\nsave_file();\nsend_network_data();\n\n{\n    std::lock_guard lock(mutex);\n\n    update_shared_state();\n}\n",[31,2918,2919,2925,2931,2937,2941,2945,2955,2959,2966],{"__ignoreMap":29},[34,2920,2921,2923],{"class":36,"line":37},[34,2922,2836],{"class":40},[34,2924,353],{"class":44},[34,2926,2927,2929],{"class":36,"line":172},[34,2928,2843],{"class":40},[34,2930,353],{"class":44},[34,2932,2933,2935],{"class":36,"line":180},[34,2934,2850],{"class":40},[34,2936,353],{"class":44},[34,2938,2939],{"class":36,"line":187},[34,2940,184],{"emptyLinePlaceholder":183},[34,2942,2943],{"class":36,"line":206},[34,2944,226],{"class":44},[34,2946,2947,2949,2951,2953],{"class":36,"line":211},[34,2948,315],{"class":40},[34,2950,2738],{"class":44},[34,2952,918],{"class":40},[34,2954,2110],{"class":44},[34,2956,2957],{"class":36,"line":223},[34,2958,184],{"emptyLinePlaceholder":183},[34,2960,2961,2964],{"class":36,"line":229},[34,2962,2963],{"class":40},"    update_shared_state",[34,2965,353],{"class":44},[34,2967,2968],{"class":36,"line":265},[34,2969,289],{"class":44},[12,2971,2972],{},"因此通常应该：",[50,2974,2975],{},[12,2976,2977],{},"只把真正需要互斥保护的操作放进临界区。",[12,2979,2980],{},"尤其要谨慎在持锁期间执行：",[67,2982,2983,2986,2991,2994,2997,3000],{},[70,2984,2985],{},"长时间计算；",[70,2987,2988,91],{},[31,2989,2990],{},"sleep",[70,2992,2993],{},"文件 IO；",[70,2995,2996],{},"网络 IO；",[70,2998,2999],{},"等待其他线程；",[70,3001,3002],{},"执行耗时未知的函数。",[12,3004,3005],{},"否则其他线程会长时间拿不到锁。",[146,3007],{},[470,3009,3010],{"id":3010},"但临界区也不能乱拆",[12,3012,3013],{},"“临界区尽量小”并不是说：",[50,3015,3016],{},[12,3017,3018],{},"每一行代码都单独加一次锁。",[12,3020,665],{},[24,3022,3024],{"className":26,"code":3023,"language":28,"meta":29,"style":29},"int count;\ndouble average;\n",[31,3025,3026,3033],{"__ignoreMap":29},[34,3027,3028,3030],{"class":36,"line":37},[34,3029,190],{"class":164},[34,3031,3032],{"class":44}," count;\n",[34,3034,3035,3038],{"class":36,"line":172},[34,3036,3037],{"class":164},"double",[34,3039,3040],{"class":44}," average;\n",[12,3042,3043],{},"并且这两个值必须始终保持对应关系。",[12,3045,715],{},[24,3047,3048],{"className":26,"code":731,"language":28,"meta":29,"style":29},[31,3049,3050,3056],{"__ignoreMap":29},[34,3051,3052,3054],{"class":36,"line":37},[34,3053,259],{"class":164},[34,3055,740],{"class":44},[34,3057,3058,3060,3062,3064],{"class":36,"line":172},[34,3059,745],{"class":44},[34,3061,196],{"class":164},[34,3063,750],{"class":40},[34,3065,353],{"class":44},[12,3067,3068],{},"那么这两个操作可能应该属于同一个临界区：",[24,3070,3072],{"className":26,"code":3071,"language":28,"meta":29,"style":29},"std::lock_guard lock(state_mutex);\n\n++count;\naverage = calculate_average();\n",[31,3073,3074,3091,3095,3101],{"__ignoreMap":29},[34,3075,3076,3078,3080,3082,3084,3086,3089],{"class":36,"line":37},[34,3077,41],{"class":40},[34,3079,376],{"class":44},[34,3081,2257],{"class":40},[34,3083,2226],{"class":40},[34,3085,382],{"class":44},[34,3087,3088],{"class":40},"state_mutex",[34,3090,2278],{"class":44},[34,3092,3093],{"class":36,"line":172},[34,3094,184],{"emptyLinePlaceholder":183},[34,3096,3097,3099],{"class":36,"line":180},[34,3098,259],{"class":164},[34,3100,740],{"class":44},[34,3102,3103,3105,3107,3109],{"class":36,"line":187},[34,3104,745],{"class":44},[34,3106,196],{"class":164},[34,3108,750],{"class":40},[34,3110,353],{"class":44},[12,3112,3113],{},"如果拆成：",[24,3115,3117],{"className":26,"code":3116,"language":28,"meta":29,"style":29},"{\n    std::lock_guard lock(state_mutex);\n    ++count;\n}\n\n{\n    std::lock_guard lock(state_mutex);\n    average = calculate_average();\n}\n",[31,3118,3119,3123,3134,3140,3144,3148,3152,3162,3173],{"__ignoreMap":29},[34,3120,3121],{"class":36,"line":37},[34,3122,226],{"class":44},[34,3124,3125,3127,3129,3131],{"class":36,"line":172},[34,3126,315],{"class":40},[34,3128,2738],{"class":44},[34,3130,918],{"class":40},[34,3132,3133],{"class":44},"(state_mutex);\n",[34,3135,3136,3138],{"class":36,"line":180},[34,3137,1287],{"class":164},[34,3139,740],{"class":44},[34,3141,3142],{"class":36,"line":187},[34,3143,289],{"class":44},[34,3145,3146],{"class":36,"line":206},[34,3147,184],{"emptyLinePlaceholder":183},[34,3149,3150],{"class":36,"line":211},[34,3151,226],{"class":44},[34,3153,3154,3156,3158,3160],{"class":36,"line":223},[34,3155,315],{"class":40},[34,3157,2738],{"class":44},[34,3159,918],{"class":40},[34,3161,3133],{"class":44},[34,3163,3164,3167,3169,3171],{"class":36,"line":229},[34,3165,3166],{"class":44},"    average ",[34,3168,196],{"class":164},[34,3170,750],{"class":40},[34,3172,353],{"class":44},[34,3174,3175],{"class":36,"line":265},[34,3176,289],{"class":44},[12,3178,3179],{},"两次加锁之间，另一个线程可能插进来读取：",[24,3181,3184],{"className":3182,"code":3183,"language":404,"meta":29},[402],"新的 count\n+\n旧的 average\n",[31,3185,3183],{"__ignoreMap":29},[12,3187,3188],{},"于是看到一个逻辑上不一致的状态。",[12,3190,1112],{},[50,3192,3193],{},[12,3194,3195],{},"临界区应该尽量小，但必须保证一次完整的共享状态修改不会被拆开。",[146,3197],{},[470,3199,3201],{"id":3200},"锁保护的是共享状态","锁保护的是“共享状态”",[12,3203,3204],{},"假设：",[24,3206,3208],{"className":26,"code":3207,"language":28,"meta":29,"style":29},"struct User\n{\n    std::string name;\n    int age;\n};\n",[31,3209,3210,3218,3222,3229,3237],{"__ignoreMap":29},[34,3211,3212,3215],{"class":36,"line":37},[34,3213,3214],{"class":164},"struct",[34,3216,3217],{"class":40}," User\n",[34,3219,3220],{"class":36,"line":172},[34,3221,226],{"class":44},[34,3223,3224,3226],{"class":36,"line":180},[34,3225,315],{"class":40},[34,3227,3228],{"class":44},"::string name;\n",[34,3230,3231,3234],{"class":36,"line":187},[34,3232,3233],{"class":164},"    int",[34,3235,3236],{"class":44}," age;\n",[34,3238,3239],{"class":36,"line":206},[34,3240,3241],{"class":44},"};\n",[12,3243,578],{},[24,3245,3247],{"className":26,"code":3246,"language":28,"meta":29,"style":29},"name\nage\n",[31,3248,3249,3254],{"__ignoreMap":29},[34,3250,3251],{"class":36,"line":37},[34,3252,3253],{"class":44},"name\n",[34,3255,3256],{"class":36,"line":172},[34,3257,3258],{"class":44},"age\n",[12,3260,3261],{},"共同组成一个完整的用户状态，那么更合理的是：",[24,3263,3265],{"className":26,"code":3264,"language":28,"meta":29,"style":29},"std::mutex user_mutex;\nUser user;\n",[31,3266,3267,3274],{"__ignoreMap":29},[34,3268,3269,3271],{"class":36,"line":37},[34,3270,41],{"class":40},[34,3272,3273],{"class":44},"::mutex user_mutex;\n",[34,3275,3276],{"class":36,"line":172},[34,3277,3278],{"class":44},"User user;\n",[12,3280,3281],{},"然后：",[24,3283,3285],{"className":26,"code":3284,"language":28,"meta":29,"style":29},"void update_user()\n{\n    std::lock_guard lock(user_mutex);\n\n    user.name = \"Tom\";\n    user.age = 20;\n}\n",[31,3286,3287,3296,3300,3311,3315,3327,3339],{"__ignoreMap":29},[34,3288,3289,3291,3294],{"class":36,"line":37},[34,3290,214],{"class":164},[34,3292,3293],{"class":40}," update_user",[34,3295,220],{"class":44},[34,3297,3298],{"class":36,"line":172},[34,3299,226],{"class":44},[34,3301,3302,3304,3306,3308],{"class":36,"line":180},[34,3303,315],{"class":40},[34,3305,2738],{"class":44},[34,3307,918],{"class":40},[34,3309,3310],{"class":44},"(user_mutex);\n",[34,3312,3313],{"class":36,"line":187},[34,3314,184],{"emptyLinePlaceholder":183},[34,3316,3317,3320,3322,3325],{"class":36,"line":206},[34,3318,3319],{"class":44},"    user.name ",[34,3321,196],{"class":164},[34,3323,3324],{"class":168}," \"Tom\"",[34,3326,203],{"class":44},[34,3328,3329,3332,3334,3337],{"class":36,"line":211},[34,3330,3331],{"class":44},"    user.age ",[34,3333,196],{"class":164},[34,3335,3336],{"class":199}," 20",[34,3338,203],{"class":44},[34,3340,3341],{"class":36,"line":223},[34,3342,289],{"class":44},[12,3344,3345],{},"而不是：",[24,3347,3350],{"className":3348,"code":3349,"language":404,"meta":29},[402],"name 一把锁\nage 一把锁\n",[31,3351,3349],{"__ignoreMap":29},[12,3353,3354],{},"所以设计 mutex 时不要只想：",[50,3356,3357],{},[12,3358,3359],{},"哪一行代码要锁？",[12,3361,3362],{},"更应该考虑：",[50,3364,3365],{},[12,3366,3367],{},"哪些数据共同组成一个必须保持一致的共享状态？",[146,3369],{},[470,3371,3373],{"id":3372},"减少共享数据通常比疯狂加锁更好","减少共享数据，通常比疯狂加锁更好",[12,3375,3376],{},"再看这个例子：",[24,3378,3380],{"className":26,"code":3379,"language":28,"meta":29,"style":29},"for (int i = 0; i \u003C 100000; ++i)\n{\n    std::lock_guard lock(counter_mutex);\n    ++counter;\n}\n",[31,3381,3382,3409,3413,3423,3429],{"__ignoreMap":29},[34,3383,3384,3387,3389,3391,3393,3395,3397,3399,3401,3403,3405,3407],{"class":36,"line":37},[34,3385,3386],{"class":164},"for",[34,3388,235],{"class":44},[34,3390,190],{"class":164},[34,3392,240],{"class":44},[34,3394,196],{"class":164},[34,3396,200],{"class":199},[34,3398,247],{"class":44},[34,3400,250],{"class":164},[34,3402,253],{"class":199},[34,3404,256],{"class":44},[34,3406,259],{"class":164},[34,3408,262],{"class":44},[34,3410,3411],{"class":36,"line":172},[34,3412,226],{"class":44},[34,3414,3415,3417,3419,3421],{"class":36,"line":180},[34,3416,315],{"class":40},[34,3418,2738],{"class":44},[34,3420,918],{"class":40},[34,3422,2494],{"class":44},[34,3424,3425,3427],{"class":36,"line":187},[34,3426,1287],{"class":164},[34,3428,277],{"class":44},[34,3430,3431],{"class":36,"line":206},[34,3432,289],{"class":44},[12,3434,3435],{},"虽然是正确的，但会反复：",[24,3437,3440],{"className":3438,"code":3439,"language":404,"meta":29},[402],"lock\nunlock\nlock\nunlock\nlock\nunlock\n...\n",[31,3441,3439],{"__ignoreMap":29},[12,3443,3444],{},"可以考虑改成：",[24,3446,3448],{"className":26,"code":3447,"language":28,"meta":29,"style":29},"void add_many()\n{\n    int local_counter = 0;\n\n    for (int i = 0; i \u003C 100000; ++i)\n    {\n        ++local_counter;\n    }\n\n    std::lock_guard lock(counter_mutex);\n\n    counter += local_counter;\n}\n",[31,3449,3450,3458,3462,3475,3479,3505,3509,3516,3520,3524,3534,3538,3549],{"__ignoreMap":29},[34,3451,3452,3454,3456],{"class":36,"line":37},[34,3453,214],{"class":164},[34,3455,217],{"class":40},[34,3457,220],{"class":44},[34,3459,3460],{"class":36,"line":172},[34,3461,226],{"class":44},[34,3463,3464,3466,3469,3471,3473],{"class":36,"line":180},[34,3465,3233],{"class":164},[34,3467,3468],{"class":44}," local_counter ",[34,3470,196],{"class":164},[34,3472,200],{"class":199},[34,3474,203],{"class":44},[34,3476,3477],{"class":36,"line":187},[34,3478,184],{"emptyLinePlaceholder":183},[34,3480,3481,3483,3485,3487,3489,3491,3493,3495,3497,3499,3501,3503],{"class":36,"line":206},[34,3482,232],{"class":164},[34,3484,235],{"class":44},[34,3486,190],{"class":164},[34,3488,240],{"class":44},[34,3490,196],{"class":164},[34,3492,200],{"class":199},[34,3494,247],{"class":44},[34,3496,250],{"class":164},[34,3498,253],{"class":199},[34,3500,256],{"class":44},[34,3502,259],{"class":164},[34,3504,262],{"class":44},[34,3506,3507],{"class":36,"line":211},[34,3508,268],{"class":44},[34,3510,3511,3513],{"class":36,"line":223},[34,3512,274],{"class":164},[34,3514,3515],{"class":44},"local_counter;\n",[34,3517,3518],{"class":36,"line":229},[34,3519,283],{"class":44},[34,3521,3522],{"class":36,"line":265},[34,3523,184],{"emptyLinePlaceholder":183},[34,3525,3526,3528,3530,3532],{"class":36,"line":271},[34,3527,315],{"class":40},[34,3529,2738],{"class":44},[34,3531,918],{"class":40},[34,3533,2494],{"class":44},[34,3535,3536],{"class":36,"line":280},[34,3537,184],{"emptyLinePlaceholder":183},[34,3539,3540,3543,3546],{"class":36,"line":286},[34,3541,3542],{"class":44},"    counter ",[34,3544,3545],{"class":164},"+=",[34,3547,3548],{"class":44}," local_counter;\n",[34,3550,3551],{"class":36,"line":292},[34,3552,289],{"class":44},[12,3554,3555],{},"这里：",[24,3557,3559],{"className":26,"code":3558,"language":28,"meta":29,"style":29},"local_counter\n",[31,3560,3561],{"__ignoreMap":29},[34,3562,3563],{"class":36,"line":37},[34,3564,3558],{"class":44},[12,3566,3567],{},"只被当前线程使用，所以不需要锁。",[12,3569,3570],{},"整个线程先自己完成：",[24,3572,3575],{"className":3573,"code":3574,"language":404,"meta":29},[402],"100000 次本地计算\n",[31,3576,3574],{"__ignoreMap":29},[12,3578,3579],{},"最后才：",[24,3581,3584],{"className":3582,"code":3583,"language":404,"meta":29},[402],"加一次锁\n↓\n更新共享 counter\n",[31,3585,3583],{"__ignoreMap":29},[12,3587,3588],{},"这反映了并发编程中的一个重要思想：",[50,3590,3591],{},[12,3592,3593],{},"能不共享的数据，就尽量不要共享。",[12,3595,3596],{},"因为：",[24,3598,3601],{"className":3599,"code":3600,"language":404,"meta":29},[402],"没有共享\n↓\n通常也就不需要同步\n",[31,3602,3600],{"__ignoreMap":29},[146,3604],{},[149,3606,3608,3610],{"id":3607},"stdunique_lock-常用",[31,3609,113],{}," （常用）",[12,3612,3613,3615],{},[31,3614,2257],{}," 的特点非常简单：",[24,3617,3620],{"className":3618,"code":3619,"language":404,"meta":29},[402],"创建\n↓\n加锁\n\n销毁\n↓\n解锁\n",[31,3621,3619],{"__ignoreMap":29},[12,3623,3624],{},"但有时候我们希望更加灵活。",[12,3626,715],{},[24,3628,3631],{"className":3629,"code":3630,"language":404,"meta":29},[402],"先获得锁\n↓\n读取共享数据\n↓\n提前释放锁\n↓\n继续执行耗时操作\n",[31,3632,3630],{"__ignoreMap":29},[12,3634,3635],{},"这时候可以使用：",[24,3637,3639],{"className":26,"code":3638,"language":28,"meta":29,"style":29},"std::unique_lock\n",[31,3640,3641],{"__ignoreMap":29},[34,3642,3643,3645],{"class":36,"line":37},[34,3644,41],{"class":40},[34,3646,3647],{"class":44},"::unique_lock\n",[12,3649,3650],{},"最简单的写法：",[24,3652,3654],{"className":26,"code":3653,"language":28,"meta":29,"style":29},"std::unique_lock\u003Cstd::mutex> lock(mutex);\n",[31,3655,3656],{"__ignoreMap":29},[34,3657,3658,3660,3662,3665,3667,3669,3671,3673,3675,3677,3679,3681],{"class":36,"line":37},[34,3659,41],{"class":40},[34,3661,376],{"class":44},[34,3663,3664],{"class":40},"unique_lock",[34,3666,250],{"class":44},[34,3668,41],{"class":40},[34,3670,376],{"class":44},[34,3672,2266],{"class":40},[34,3674,2269],{"class":44},[34,3676,918],{"class":40},[34,3678,382],{"class":44},[34,3680,2266],{"class":40},[34,3682,2278],{"class":44},[12,3684,3685,3686,3688],{},"和 ",[31,3687,2257],{}," 一样：",[24,3690,3693],{"className":3691,"code":3692,"language":404,"meta":29},[402],"构造时获得 mutex\n析构时自动释放 mutex\n",[31,3694,3692],{"__ignoreMap":29},[12,3696,3697,3698,3700],{},"但 ",[31,3699,3664],{}," 提供了更多控制能力。",[146,3702],{},[470,3704,3706,3708],{"id":3705},"unique_lock-可以提前解锁",[31,3707,3664],{}," 可以提前解锁",[12,3710,715],{},[24,3712,3714],{"className":26,"code":3713,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex);\n\nread_shared_state();\n\nlock.unlock();\n\ndo_expensive_work();\n",[31,3715,3716,3732,3736,3743,3747,3756,3760],{"__ignoreMap":29},[34,3717,3718,3720,3722,3724,3726,3728,3730],{"class":36,"line":37},[34,3719,41],{"class":40},[34,3721,376],{"class":44},[34,3723,3664],{"class":40},[34,3725,2226],{"class":40},[34,3727,382],{"class":44},[34,3729,2266],{"class":40},[34,3731,2278],{"class":44},[34,3733,3734],{"class":36,"line":172},[34,3735,184],{"emptyLinePlaceholder":183},[34,3737,3738,3741],{"class":36,"line":180},[34,3739,3740],{"class":40},"read_shared_state",[34,3742,353],{"class":44},[34,3744,3745],{"class":36,"line":187},[34,3746,184],{"emptyLinePlaceholder":183},[34,3748,3749,3752,3754],{"class":36,"line":206},[34,3750,3751],{"class":44},"lock.",[34,3753,933],{"class":40},[34,3755,353],{"class":44},[34,3757,3758],{"class":36,"line":211},[34,3759,184],{"emptyLinePlaceholder":183},[34,3761,3762,3765],{"class":36,"line":223},[34,3763,3764],{"class":40},"do_expensive_work",[34,3766,353],{"class":44},[12,3768,2775],{},[24,3770,3773],{"className":3771,"code":3772,"language":404,"meta":29},[402],"获得 mutex\n↓\n访问共享数据\n↓\n主动释放 mutex\n↓\n执行耗时操作\n",[31,3774,3772],{"__ignoreMap":29},[12,3776,3777,3778,3780],{},"而 ",[31,3779,2257],{}," 不提供这种手动解锁能力。",[146,3782],{},[470,3784,3786,3788],{"id":3785},"unique_lock-可以再次加锁",[31,3787,3664],{}," 可以再次加锁",[12,3790,715],{},[24,3792,3794],{"className":26,"code":3793,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex);\n\nread_shared_state();\n\nlock.unlock();\n\ndo_something();\n\nlock.lock();\n\nchange_shared_state();\n",[31,3795,3796,3812,3816,3822,3826,3834,3838,3845,3849,3857,3861],{"__ignoreMap":29},[34,3797,3798,3800,3802,3804,3806,3808,3810],{"class":36,"line":37},[34,3799,41],{"class":40},[34,3801,376],{"class":44},[34,3803,3664],{"class":40},[34,3805,2226],{"class":40},[34,3807,382],{"class":44},[34,3809,2266],{"class":40},[34,3811,2278],{"class":44},[34,3813,3814],{"class":36,"line":172},[34,3815,184],{"emptyLinePlaceholder":183},[34,3817,3818,3820],{"class":36,"line":180},[34,3819,3740],{"class":40},[34,3821,353],{"class":44},[34,3823,3824],{"class":36,"line":187},[34,3825,184],{"emptyLinePlaceholder":183},[34,3827,3828,3830,3832],{"class":36,"line":206},[34,3829,3751],{"class":44},[34,3831,933],{"class":40},[34,3833,353],{"class":44},[34,3835,3836],{"class":36,"line":211},[34,3837,184],{"emptyLinePlaceholder":183},[34,3839,3840,3843],{"class":36,"line":223},[34,3841,3842],{"class":40},"do_something",[34,3844,353],{"class":44},[34,3846,3847],{"class":36,"line":229},[34,3848,184],{"emptyLinePlaceholder":183},[34,3850,3851,3853,3855],{"class":36,"line":265},[34,3852,3751],{"class":44},[34,3854,918],{"class":40},[34,3856,353],{"class":44},[34,3858,3859],{"class":36,"line":271},[34,3860,184],{"emptyLinePlaceholder":183},[34,3862,3863,3865],{"class":36,"line":280},[34,3864,1791],{"class":40},[34,3866,353],{"class":44},[12,3868,3869,3870,3872],{},"所以 ",[31,3871,3664],{}," 可以进行：",[24,3874,3877],{"className":3875,"code":3876,"language":404,"meta":29},[402],"lock\n↓\nunlock\n↓\nlock\n↓\nunlock\n",[31,3878,3876],{"__ignoreMap":29},[12,3880,3881],{},"不过这种代码越复杂，锁的设计也越容易出问题。",[12,3883,3884],{},"因此只有真的需要这种灵活性时才使用。",[146,3886],{},[470,3888,3890,3891],{"id":3889},"延迟加锁stddefer_lock","延迟加锁：",[31,3892,3893],{},"std::defer_lock",[12,3895,3896],{},"默认：",[24,3898,3900],{"className":26,"code":3899,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex);\n",[31,3901,3902],{"__ignoreMap":29},[34,3903,3904,3906,3908,3910,3912,3914,3916],{"class":36,"line":37},[34,3905,41],{"class":40},[34,3907,376],{"class":44},[34,3909,3664],{"class":40},[34,3911,2226],{"class":40},[34,3913,382],{"class":44},[34,3915,2266],{"class":40},[34,3917,2278],{"class":44},[12,3919,3920],{},"会立即获得 mutex。",[12,3922,3923],{},"但可以写：",[24,3925,3927],{"className":26,"code":3926,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex, std::defer_lock);\n",[31,3928,3929],{"__ignoreMap":29},[34,3930,3931,3933,3935,3937,3939,3941,3943,3946,3948,3950,3953],{"class":36,"line":37},[34,3932,41],{"class":40},[34,3934,376],{"class":44},[34,3936,3664],{"class":40},[34,3938,2226],{"class":40},[34,3940,382],{"class":44},[34,3942,2266],{"class":40},[34,3944,3945],{"class":44},", ",[34,3947,41],{"class":40},[34,3949,376],{"class":44},[34,3951,3952],{"class":40},"defer_lock",[34,3954,2278],{"class":44},[12,3956,3957],{},"此时：",[24,3959,3962],{"className":3960,"code":3961,"language":404,"meta":29},[402],"unique_lock 对象已经创建\n但是 mutex 还没有被锁住\n",[31,3963,3961],{"__ignoreMap":29},[12,3965,3966],{},"之后再：",[24,3968,3970],{"className":26,"code":3969,"language":28,"meta":29,"style":29},"lock.lock();\n",[31,3971,3972],{"__ignoreMap":29},[34,3973,3974,3976,3978],{"class":36,"line":37},[34,3975,3751],{"class":44},[34,3977,918],{"class":40},[34,3979,353],{"class":44},[12,3981,715],{},[24,3983,3985],{"className":26,"code":3984,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex, std::defer_lock);\n\ndo_something();\n\nlock.lock();\n\nchange_shared_state();\n",[31,3986,3987,4011,4015,4021,4025,4033,4037],{"__ignoreMap":29},[34,3988,3989,3991,3993,3995,3997,3999,4001,4003,4005,4007,4009],{"class":36,"line":37},[34,3990,41],{"class":40},[34,3992,376],{"class":44},[34,3994,3664],{"class":40},[34,3996,2226],{"class":40},[34,3998,382],{"class":44},[34,4000,2266],{"class":40},[34,4002,3945],{"class":44},[34,4004,41],{"class":40},[34,4006,376],{"class":44},[34,4008,3952],{"class":40},[34,4010,2278],{"class":44},[34,4012,4013],{"class":36,"line":172},[34,4014,184],{"emptyLinePlaceholder":183},[34,4016,4017,4019],{"class":36,"line":180},[34,4018,3842],{"class":40},[34,4020,353],{"class":44},[34,4022,4023],{"class":36,"line":187},[34,4024,184],{"emptyLinePlaceholder":183},[34,4026,4027,4029,4031],{"class":36,"line":206},[34,4028,3751],{"class":44},[34,4030,918],{"class":40},[34,4032,353],{"class":44},[34,4034,4035],{"class":36,"line":211},[34,4036,184],{"emptyLinePlaceholder":183},[34,4038,4039,4041],{"class":36,"line":223},[34,4040,1791],{"class":40},[34,4042,353],{"class":44},[146,4044],{},[470,4046,4048,4049],{"id":4047},"尝试加锁stdtry_to_lock","尝试加锁：",[31,4050,4051],{},"std::try_to_lock",[12,4053,4054],{},"还可以：",[24,4056,4058],{"className":26,"code":4057,"language":28,"meta":29,"style":29},"std::unique_lock lock(mutex, std::try_to_lock);\n",[31,4059,4060],{"__ignoreMap":29},[34,4061,4062,4064,4066,4068,4070,4072,4074,4076,4078,4080,4083],{"class":36,"line":37},[34,4063,41],{"class":40},[34,4065,376],{"class":44},[34,4067,3664],{"class":40},[34,4069,2226],{"class":40},[34,4071,382],{"class":44},[34,4073,2266],{"class":40},[34,4075,3945],{"class":44},[34,4077,41],{"class":40},[34,4079,376],{"class":44},[34,4081,4082],{"class":40},"try_to_lock",[34,4084,2278],{"class":44},[12,4086,4087],{},"它会尝试获得 mutex，但不会因为暂时拿不到锁而一直等待。",[12,4089,4090],{},"之后可以：",[24,4092,4094],{"className":26,"code":4093,"language":28,"meta":29,"style":29},"if (lock.owns_lock())\n{\n    \u002F\u002F 已经获得 mutex\n}\nelse\n{\n    \u002F\u002F 当前没有获得 mutex\n}\n",[31,4095,4096,4108,4112,4117,4121,4125,4129,4134],{"__ignoreMap":29},[34,4097,4098,4100,4103,4106],{"class":36,"line":37},[34,4099,1270],{"class":164},[34,4101,4102],{"class":44}," (lock.",[34,4104,4105],{"class":40},"owns_lock",[34,4107,1278],{"class":44},[34,4109,4110],{"class":36,"line":172},[34,4111,226],{"class":44},[34,4113,4114],{"class":36,"line":180},[34,4115,4116],{"class":1320},"    \u002F\u002F 已经获得 mutex\n",[34,4118,4119],{"class":36,"line":187},[34,4120,289],{"class":44},[34,4122,4123],{"class":36,"line":206},[34,4124,1311],{"class":164},[34,4126,4127],{"class":36,"line":211},[34,4128,226],{"class":44},[34,4130,4131],{"class":36,"line":223},[34,4132,4133],{"class":1320},"    \u002F\u002F 当前没有获得 mutex\n",[34,4135,4136],{"class":36,"line":229},[34,4137,289],{"class":44},[146,4139],{},[470,4141,4142],{"id":4105},[31,4143,4144],{},"owns_lock()",[12,4146,4147,4148,4150],{},"由于 ",[31,4149,3664],{}," 比较灵活，所以一个：",[24,4152,4154],{"className":26,"code":4153,"language":28,"meta":29,"style":29},"unique_lock\n",[31,4155,4156],{"__ignoreMap":29},[34,4157,4158],{"class":36,"line":37},[34,4159,4153],{"class":44},[12,4161,4162],{},"对象不一定始终持有 mutex。",[12,4164,715],{},[24,4166,4167],{"className":26,"code":3926,"language":28,"meta":29,"style":29},[31,4168,4169],{"__ignoreMap":29},[34,4170,4171,4173,4175,4177,4179,4181,4183,4185,4187,4189,4191],{"class":36,"line":37},[34,4172,41],{"class":40},[34,4174,376],{"class":44},[34,4176,3664],{"class":40},[34,4178,2226],{"class":40},[34,4180,382],{"class":44},[34,4182,2266],{"class":40},[34,4184,3945],{"class":44},[34,4186,41],{"class":40},[34,4188,376],{"class":44},[34,4190,3952],{"class":40},[34,4192,2278],{"class":44},[12,4194,4195],{},"刚创建时：",[24,4197,4199],{"className":26,"code":4198,"language":28,"meta":29,"style":29},"lock.owns_lock()\n",[31,4200,4201],{"__ignoreMap":29},[34,4202,4203,4205,4207],{"class":36,"line":37},[34,4204,3751],{"class":44},[34,4206,4105],{"class":40},[34,4208,220],{"class":44},[12,4210,4211],{},"为：",[24,4213,4214],{"className":26,"code":1361,"language":28,"meta":29,"style":29},[31,4215,4216],{"__ignoreMap":29},[34,4217,4218],{"class":36,"line":37},[34,4219,1361],{"class":199},[12,4221,2243],{},[24,4223,4224],{"className":26,"code":3969,"language":28,"meta":29,"style":29},[31,4225,4226],{"__ignoreMap":29},[34,4227,4228,4230,4232],{"class":36,"line":37},[34,4229,3751],{"class":44},[34,4231,918],{"class":40},[34,4233,353],{"class":44},[12,4235,4236],{},"以后：",[24,4238,4239],{"className":26,"code":4198,"language":28,"meta":29,"style":29},[31,4240,4241],{"__ignoreMap":29},[34,4242,4243,4245,4247],{"class":36,"line":37},[34,4244,3751],{"class":44},[34,4246,4105],{"class":40},[34,4248,220],{"class":44},[12,4250,4251],{},"才为：",[24,4253,4254],{"className":26,"code":1349,"language":28,"meta":29,"style":29},[31,4255,4256],{"__ignoreMap":29},[34,4257,4258],{"class":36,"line":37},[34,4259,1349],{"class":199},[12,4261,4262],{},"如果再：",[24,4264,4266],{"className":26,"code":4265,"language":28,"meta":29,"style":29},"lock.unlock();\n",[31,4267,4268],{"__ignoreMap":29},[34,4269,4270,4272,4274],{"class":36,"line":37},[34,4271,3751],{"class":44},[34,4273,933],{"class":40},[34,4275,353],{"class":44},[12,4277,4278],{},"又会变回：",[24,4280,4281],{"className":26,"code":1361,"language":28,"meta":29,"style":29},[31,4282,4283],{"__ignoreMap":29},[34,4284,4285],{"class":36,"line":37},[34,4286,1361],{"class":199},[146,4288],{},[470,4290,4292,129,4294,4296],{"id":4291},"lock_guard-和-unique_lock-怎么选",[31,4293,2257],{},[31,4295,3664],{}," 怎么选",[12,4298,4299],{},"初学阶段可以直接记：",[4301,4302,4303,4319],"table",{},[4304,4305,4306],"thead",{},[4307,4308,4309,4313,4316],"tr",{},[4310,4311,4312],"th",{},"工具",[4310,4314,4315],{},"特点",[4310,4317,4318],{},"常见场景",[4320,4321,4322,4335],"tbody",{},[4307,4323,4324,4329,4332],{},[4325,4326,4327],"td",{},[31,4328,107],{},[4325,4330,4331],{},"简单，整个作用域始终持锁",[4325,4333,4334],{},"普通临界区",[4307,4336,4337,4341,4344],{},[4325,4338,4339],{},[31,4340,113],{},[4325,4342,4343],{},"可以主动解锁、重新加锁、延迟加锁、尝试加锁",[4325,4345,4346],{},"需要灵活管理锁生命周期",[12,4348,4349],{},"所以通常：",[24,4351,4354],{"className":4352,"code":4353,"language":404,"meta":29},[402],"普通情况\n↓\nlock_guard\n",[31,4355,4353],{"__ignoreMap":29},[12,4357,4358],{},"需要：",[24,4360,4363],{"className":4361,"code":4362,"language":404,"meta":29},[402],"unlock()\n重新 lock()\ndefer_lock\ntry_to_lock\n",[31,4364,4362],{"__ignoreMap":29},[12,4366,4367],{},"等能力时：",[24,4369,4371],{"className":4370,"code":4153,"language":404,"meta":29},[402],[31,4372,4153],{"__ignoreMap":29},[12,4374,4375,4376,4378],{},"不要因为 ",[31,4377,3664],{}," 功能更多，就认为：",[50,4380,4381],{},[12,4382,4383,4384,4386],{},"它永远比 ",[31,4385,2257],{}," 更好。",[12,4388,4389],{},"功能更多也意味着状态更复杂。",[12,4391,4392],{},"能使用简单方案时，一般优先简单方案。",[146,4394],{},[4301,4396,4397,4412],{},[4304,4398,4399],{},[4307,4400,4401,4404,4408],{},[4310,4402,4403],{},"项目",[4310,4405,4406],{},[31,4407,2257],{},[4310,4409,4410],{},[31,4411,3664],{},[4320,4413,4414,4424,4438,4447,4457,4466,4477,4488,4501],{},[4307,4415,4416,4419,4422],{},[4325,4417,4418],{},"自动加锁\u002F解锁",[4325,4420,4421],{},"✅",[4325,4423,4421],{},[4307,4425,4426,4433,4436],{},[4325,4427,4428,4429,97,4431],{},"手动 ",[31,4430,90],{},[31,4432,83],{},[4325,4434,4435],{},"❌",[4325,4437,4421],{},[4307,4439,4440,4443,4445],{},[4325,4441,4442],{},"延迟加锁",[4325,4444,4435],{},[4325,4446,4421],{},[4307,4448,4449,4453,4455],{},[4325,4450,4451],{},[31,4452,1037],{},[4325,4454,4435],{},[4325,4456,4421],{},[4307,4458,4459,4462,4464],{},[4325,4460,4461],{},"move",[4325,4463,4435],{},[4325,4465,4421],{},[4307,4467,4468,4473,4475],{},[4325,4469,4470],{},[31,4471,4472],{},"condition_variable",[4325,4474,4435],{},[4325,4476,4421],{},[4307,4478,4479,4482,4485],{},[4325,4480,4481],{},"对象大小",[4325,4483,4484],{},"更小",[4325,4486,4487],{},"通常更大",[4307,4489,4490,4493,4498],{},[4325,4491,4492],{},"理论运行开销",[4325,4494,4495],{},[16,4496,4497],{},"最低",[4325,4499,4500],{},"略高",[4307,4502,4503,4506,4509],{},[4325,4504,4505],{},"灵活性",[4325,4507,4508],{},"低",[4325,4510,4511],{},[16,4512,4513],{},"高",[12,4515,4516],{},"单论效率，std::lock_guard 略高于 std::unique_lock，但通常差距小到基本不用考虑。",[149,4518,4519],{"id":4519},"死锁与多把锁",[470,4521,4522],{"id":4522},"什么是死锁",[12,4524,4525],{},"mutex 可以解决很多线程同时访问共享数据的问题。",[12,4527,4528],{},"但如果同时使用多把 mutex，又可能产生新的问题：",[50,4530,4531],{},[12,4532,4533,4534,4541],{},"死锁（Deadlock），当然不是V社开发的那个游戏",[4535,4536,4540],"a",{"href":4537,"rel":4538},"https:\u002F\u002Fstore.steampowered.com\u002Fapp\u002F1422450\u002FDeadlock\u002F",[4539],"nofollow","死锁","。",[12,4543,3204],{},[24,4545,4547],{"className":26,"code":4546,"language":28,"meta":29,"style":29},"std::mutex m1;\nstd::mutex m2;\n",[31,4548,4549,4556],{"__ignoreMap":29},[34,4550,4551,4553],{"class":36,"line":37},[34,4552,41],{"class":40},[34,4554,4555],{"class":44},"::mutex m1;\n",[34,4557,4558,4560],{"class":36,"line":172},[34,4559,41],{"class":40},[34,4561,4562],{"class":44},"::mutex m2;\n",[12,4564,4565],{},"线程 A：",[24,4567,4569],{"className":26,"code":4568,"language":28,"meta":29,"style":29},"void task1()\n{\n    std::lock_guard lock1(m1);\n    std::lock_guard lock2(m2);\n}\n",[31,4570,4571,4580,4584,4596,4608],{"__ignoreMap":29},[34,4572,4573,4575,4578],{"class":36,"line":37},[34,4574,214],{"class":164},[34,4576,4577],{"class":40}," task1",[34,4579,220],{"class":44},[34,4581,4582],{"class":36,"line":172},[34,4583,226],{"class":44},[34,4585,4586,4588,4590,4593],{"class":36,"line":180},[34,4587,315],{"class":40},[34,4589,2738],{"class":44},[34,4591,4592],{"class":40},"lock1",[34,4594,4595],{"class":44},"(m1);\n",[34,4597,4598,4600,4602,4605],{"class":36,"line":187},[34,4599,315],{"class":40},[34,4601,2738],{"class":44},[34,4603,4604],{"class":40},"lock2",[34,4606,4607],{"class":44},"(m2);\n",[34,4609,4610],{"class":36,"line":206},[34,4611,289],{"class":44},[12,4613,4614],{},"线程 B：",[24,4616,4618],{"className":26,"code":4617,"language":28,"meta":29,"style":29},"void task2()\n{\n    std::lock_guard lock2(m2);\n    std::lock_guard lock1(m1);\n}\n",[31,4619,4620,4629,4633,4643,4653],{"__ignoreMap":29},[34,4621,4622,4624,4627],{"class":36,"line":37},[34,4623,214],{"class":164},[34,4625,4626],{"class":40}," task2",[34,4628,220],{"class":44},[34,4630,4631],{"class":36,"line":172},[34,4632,226],{"class":44},[34,4634,4635,4637,4639,4641],{"class":36,"line":180},[34,4636,315],{"class":40},[34,4638,2738],{"class":44},[34,4640,4604],{"class":40},[34,4642,4607],{"class":44},[34,4644,4645,4647,4649,4651],{"class":36,"line":187},[34,4646,315],{"class":40},[34,4648,2738],{"class":44},[34,4650,4592],{"class":40},[34,4652,4595],{"class":44},[34,4654,4655],{"class":36,"line":206},[34,4656,289],{"class":44},[12,4658,4659],{},"两个线程的锁顺序不同：",[24,4661,4664],{"className":4662,"code":4663,"language":404,"meta":29},[402],"线程 A：\nm1 → m2\n\n线程 B：\nm2 → m1\n",[31,4665,4663],{"__ignoreMap":29},[12,4667,4668],{},"于是可能发生：",[24,4670,4673],{"className":4671,"code":4672,"language":404,"meta":29},[402],"线程 A 获得 m1\n\n线程 B 获得 m2\n\n线程 A 想获得 m2\n但是 m2 在线程 B 手里\n↓\nA 等待 B\n\n线程 B 想获得 m1\n但是 m1 在线程 A 手里\n↓\nB 等待 A\n",[31,4674,4672],{"__ignoreMap":29},[12,4676,544],{},[24,4678,4681],{"className":4679,"code":4680,"language":404,"meta":29},[402],"A 等 B\nB 等 A\n",[31,4682,4680],{"__ignoreMap":29},[12,4684,4685],{},"谁都无法继续。",[12,4687,2149],{},[50,4689,4690],{},[12,4691,4540],{},[146,4693],{},[470,4695,4696],{"id":4696},"一个形象的死锁例子",[12,4698,4699],{},"可以把两把 mutex 想象成两根筷子。",[12,4701,4702],{},"两个人吃饭都必须同时拥有：",[24,4704,4707],{"className":4705,"code":4706,"language":404,"meta":29},[402],"左筷子\n+\n右筷子\n",[31,4708,4706],{"__ignoreMap":29},[12,4710,4711],{},"A 拿到了左筷子。",[12,4713,4714],{},"B 拿到了右筷子。",[12,4716,3281],{},[24,4718,4721],{"className":4719,"code":4720,"language":404,"meta":29},[402],"A：等 B 放下右筷子\n\nB：等 A 放下左筷子\n",[31,4722,4720],{"__ignoreMap":29},[12,4724,4725],{},"但两个人谁都不愿意先放下自己已经拿到的筷子。",[12,4727,1907],{},[24,4729,4732],{"className":4730,"code":4731,"language":404,"meta":29},[402],"谁也吃不上饭\n",[31,4733,4731],{"__ignoreMap":29},[12,4735,4736],{},"这和线程死锁非常类似。",[146,4738],{},[470,4740,4742],{"id":4741},"避免死锁固定加锁顺序","避免死锁：固定加锁顺序",[12,4744,4745],{},"一个非常重要的方法就是：",[50,4747,4748],{},[12,4749,4750],{},"整个程序规定统一的锁获取顺序。",[12,4752,4753],{},"例如规定：",[24,4755,4758],{"className":4756,"code":4757,"language":404,"meta":29},[402],"永远先获得 m1\n再获得 m2\n",[31,4759,4757],{"__ignoreMap":29},[12,4761,4762],{},"那么所有线程都必须：",[24,4764,4766],{"className":26,"code":4765,"language":28,"meta":29,"style":29},"std::lock_guard lock1(m1);\nstd::lock_guard lock2(m2);\n",[31,4767,4768,4786],{"__ignoreMap":29},[34,4769,4770,4772,4774,4776,4779,4781,4784],{"class":36,"line":37},[34,4771,41],{"class":40},[34,4773,376],{"class":44},[34,4775,2257],{"class":40},[34,4777,4778],{"class":40}," lock1",[34,4780,382],{"class":44},[34,4782,4783],{"class":40},"m1",[34,4785,2278],{"class":44},[34,4787,4788,4790,4792,4794,4797,4799,4802],{"class":36,"line":172},[34,4789,41],{"class":40},[34,4791,376],{"class":44},[34,4793,2257],{"class":40},[34,4795,4796],{"class":40}," lock2",[34,4798,382],{"class":44},[34,4800,4801],{"class":40},"m2",[34,4803,2278],{"class":44},[12,4805,4806],{},"而不能有些地方写：",[24,4808,4811],{"className":4809,"code":4810,"language":404,"meta":29},[402],"m1 → m2\n",[31,4812,4810],{"__ignoreMap":29},[12,4814,4815],{},"另一些地方却写：",[24,4817,4820],{"className":4818,"code":4819,"language":404,"meta":29},[402],"m2 → m1\n",[31,4821,4819],{"__ignoreMap":29},[12,4823,4824],{},"统一锁顺序可以避免很多典型死锁。",[146,4826],{},[470,4828,4830],{"id":4829},"stdscoped_lock",[31,4831,119],{},[12,4833,4834],{},"如果一个操作本来就需要：",[50,4836,4837],{},[12,4838,4839],{},"同时获得多把 mutex",[12,4841,4842],{},"C++17 提供：",[24,4844,4846],{"className":26,"code":4845,"language":28,"meta":29,"style":29},"std::scoped_lock\n",[31,4847,4848],{"__ignoreMap":29},[34,4849,4850,4852],{"class":36,"line":37},[34,4851,41],{"class":40},[34,4853,4854],{"class":44},"::scoped_lock\n",[12,4856,715],{},[24,4858,4860],{"className":26,"code":4859,"language":28,"meta":29,"style":29},"#include \u003Cmutex>\n\nstd::mutex m1;\nstd::mutex m2;\n\nvoid task()\n{\n    std::scoped_lock lock(m1, m2);\n\n    \u002F\u002F 当前同时持有 m1 和 m2\n}\n",[31,4861,4862,4868,4872,4878,4884,4888,4897,4901,4913,4917,4922],{"__ignoreMap":29},[34,4863,4864,4866],{"class":36,"line":37},[34,4865,165],{"class":164},[34,4867,1014],{"class":168},[34,4869,4870],{"class":36,"line":172},[34,4871,184],{"emptyLinePlaceholder":183},[34,4873,4874,4876],{"class":36,"line":180},[34,4875,41],{"class":40},[34,4877,4555],{"class":44},[34,4879,4880,4882],{"class":36,"line":187},[34,4881,41],{"class":40},[34,4883,4562],{"class":44},[34,4885,4886],{"class":36,"line":206},[34,4887,184],{"emptyLinePlaceholder":183},[34,4889,4890,4892,4895],{"class":36,"line":211},[34,4891,214],{"class":164},[34,4893,4894],{"class":40}," task",[34,4896,220],{"class":44},[34,4898,4899],{"class":36,"line":223},[34,4900,226],{"class":44},[34,4902,4903,4905,4908,4910],{"class":36,"line":229},[34,4904,315],{"class":40},[34,4906,4907],{"class":44},"::scoped_lock ",[34,4909,918],{"class":40},[34,4911,4912],{"class":44},"(m1, m2);\n",[34,4914,4915],{"class":36,"line":265},[34,4916,184],{"emptyLinePlaceholder":183},[34,4918,4919],{"class":36,"line":271},[34,4920,4921],{"class":1320},"    \u002F\u002F 当前同时持有 m1 和 m2\n",[34,4923,4924],{"class":36,"line":280},[34,4925,289],{"class":44},[12,4927,4928],{},"它会使用标准库提供的死锁避免策略获取这些锁。",[12,4930,4931],{},"相比：",[24,4933,4934],{"className":26,"code":4765,"language":28,"meta":29,"style":29},[31,4935,4936,4952],{"__ignoreMap":29},[34,4937,4938,4940,4942,4944,4946,4948,4950],{"class":36,"line":37},[34,4939,41],{"class":40},[34,4941,376],{"class":44},[34,4943,2257],{"class":40},[34,4945,4778],{"class":40},[34,4947,382],{"class":44},[34,4949,4783],{"class":40},[34,4951,2278],{"class":44},[34,4953,4954,4956,4958,4960,4962,4964,4966],{"class":36,"line":172},[34,4955,41],{"class":40},[34,4957,376],{"class":44},[34,4959,2257],{"class":40},[34,4961,4796],{"class":40},[34,4963,382],{"class":44},[34,4965,4801],{"class":40},[34,4967,2278],{"class":44},[12,4969,4970],{},"如果你的意图本来就是：",[24,4972,4975],{"className":4973,"code":4974,"language":404,"meta":29},[402],"我要一起拿到 m1 和 m2\n",[31,4976,4974],{"__ignoreMap":29},[12,4978,4979],{},"那么：",[24,4981,4983],{"className":26,"code":4982,"language":28,"meta":29,"style":29},"std::scoped_lock lock(m1, m2);\n",[31,4984,4985],{"__ignoreMap":29},[34,4986,4987,4989,4991,4994,4996,4998,5000,5002,5004],{"class":36,"line":37},[34,4988,41],{"class":40},[34,4990,376],{"class":44},[34,4992,4993],{"class":40},"scoped_lock",[34,4995,2226],{"class":40},[34,4997,382],{"class":44},[34,4999,4783],{"class":40},[34,5001,3945],{"class":44},[34,5003,4801],{"class":40},[34,5005,2278],{"class":44},[12,5007,5008],{},"通常更加直接。",[12,5010,5011],{},"作用域结束以后：",[24,5013,5016],{"className":5014,"code":5015,"language":404,"meta":29},[402],"scoped_lock 析构\n↓\n自动释放它管理的锁\n",[31,5017,5015],{"__ignoreMap":29},[146,5019],{},[1051,5021,5023,5025],{"id":5022},"scoped_lock-也可以管理一把锁",[31,5024,4993],{}," 也可以管理一把锁",[12,5027,5028],{},"它不一定必须传两把以上 mutex。",[12,5030,5031],{},"也可以：",[24,5033,5035],{"className":26,"code":5034,"language":28,"meta":29,"style":29},"std::scoped_lock lock(mutex);\n",[31,5036,5037],{"__ignoreMap":29},[34,5038,5039,5041,5043,5045,5047,5049,5051],{"class":36,"line":37},[34,5040,41],{"class":40},[34,5042,376],{"class":44},[34,5044,4993],{"class":40},[34,5046,2226],{"class":40},[34,5048,382],{"class":44},[34,5050,2266],{"class":40},[34,5052,2278],{"class":44},[12,5054,5055],{},"对于一把锁的普通场景，它和：",[24,5057,5059],{"className":26,"code":5058,"language":28,"meta":29,"style":29},"std::lock_guard lock(mutex);\n",[31,5060,5061],{"__ignoreMap":29},[34,5062,5063,5065,5067,5069,5071,5073,5075],{"class":36,"line":37},[34,5064,41],{"class":40},[34,5066,376],{"class":44},[34,5068,2257],{"class":40},[34,5070,2226],{"class":40},[34,5072,382],{"class":44},[34,5074,2266],{"class":40},[34,5076,2278],{"class":44},[12,5078,5079],{},"很接近。",[12,5081,5082],{},"初学阶段可以简单记：",[24,5084,5087],{"className":5085,"code":5086,"language":404,"meta":29},[402],"普通单锁\n→ lock_guard\n\n需要灵活控制\n→ unique_lock\n\n同时管理多把锁\n→ scoped_lock\n",[31,5088,5086],{"__ignoreMap":29},[146,5090],{},[470,5092,5094],{"id":5093},"stdlock",[31,5095,5096],{},"std::lock()",[12,5098,5099],{},"C++11 还提供：",[24,5101,5103],{"className":26,"code":5102,"language":28,"meta":29,"style":29},"std::lock(m1, m2);\n",[31,5104,5105],{"__ignoreMap":29},[34,5106,5107,5109,5111,5113],{"class":36,"line":37},[34,5108,41],{"class":40},[34,5110,376],{"class":44},[34,5112,918],{"class":40},[34,5114,4912],{"class":44},[12,5116,5117],{},"它可以使用死锁避免算法尝试获取多把可锁对象。",[12,5119,715],{},[24,5121,5122],{"className":26,"code":5102,"language":28,"meta":29,"style":29},[31,5123,5124],{"__ignoreMap":29},[34,5125,5126,5128,5130,5132],{"class":36,"line":37},[34,5127,41],{"class":40},[34,5129,376],{"class":44},[34,5131,918],{"class":40},[34,5133,4912],{"class":44},[12,5135,5136],{},"执行成功以后：",[24,5138,5141],{"className":5139,"code":5140,"language":404,"meta":29},[402],"m1 已经被当前线程获得\nm2 也已经被当前线程获得\n",[31,5142,5140],{"__ignoreMap":29},[12,5144,5145],{},"接下来通常需要把它们交给 RAII 对象管理。",[12,5147,715],{},[24,5149,5151],{"className":26,"code":5150,"language":28,"meta":29,"style":29},"std::lock(m1, m2);\n\nstd::lock_guard lock1(m1, std::adopt_lock);\nstd::lock_guard lock2(m2, std::adopt_lock);\n",[31,5152,5153,5163,5167,5192],{"__ignoreMap":29},[34,5154,5155,5157,5159,5161],{"class":36,"line":37},[34,5156,41],{"class":40},[34,5158,376],{"class":44},[34,5160,918],{"class":40},[34,5162,4912],{"class":44},[34,5164,5165],{"class":36,"line":172},[34,5166,184],{"emptyLinePlaceholder":183},[34,5168,5169,5171,5173,5175,5177,5179,5181,5183,5185,5187,5190],{"class":36,"line":180},[34,5170,41],{"class":40},[34,5172,376],{"class":44},[34,5174,2257],{"class":40},[34,5176,4778],{"class":40},[34,5178,382],{"class":44},[34,5180,4783],{"class":40},[34,5182,3945],{"class":44},[34,5184,41],{"class":40},[34,5186,376],{"class":44},[34,5188,5189],{"class":40},"adopt_lock",[34,5191,2278],{"class":44},[34,5193,5194,5196,5198,5200,5202,5204,5206,5208,5210,5212,5214],{"class":36,"line":187},[34,5195,41],{"class":40},[34,5197,376],{"class":44},[34,5199,2257],{"class":40},[34,5201,4796],{"class":40},[34,5203,382],{"class":44},[34,5205,4801],{"class":40},[34,5207,3945],{"class":44},[34,5209,41],{"class":40},[34,5211,376],{"class":44},[34,5213,5189],{"class":40},[34,5215,2278],{"class":44},[12,5217,3555],{},[24,5219,5221],{"className":26,"code":5220,"language":28,"meta":29,"style":29},"std::adopt_lock\n",[31,5222,5223],{"__ignoreMap":29},[34,5224,5225,5227],{"class":36,"line":37},[34,5226,41],{"class":40},[34,5228,5229],{"class":44},"::adopt_lock\n",[12,5231,5232,5233,5235],{},"可以理解为告诉 ",[31,5234,2257],{},"：",[50,5237,5238],{},[12,5239,5240,5241,5243],{},"这把 mutex 已经被我提前锁好了，你不要再调用 ",[31,5242,83],{},"，只需要从现在开始接管它，最后负责释放。",[12,5245,1112],{},[24,5247,5248],{"className":26,"code":5220,"language":28,"meta":29,"style":29},[31,5249,5250],{"__ignoreMap":29},[34,5251,5252,5254],{"class":36,"line":37},[34,5253,41],{"class":40},[34,5255,5229],{"class":44},[12,5257,5258],{},"的前提是：",[50,5260,5261],{},[12,5262,5263],{},"当前线程必须已经持有这把锁。",[12,5265,5266],{},"C++17 以后，如果只是：",[24,5268,5271],{"className":5269,"code":5270,"language":404,"meta":29},[402],"同时获得多把 mutex\n+\n自动管理它们的生命周期\n",[31,5272,5270],{"__ignoreMap":29},[12,5274,5275],{},"通常：",[24,5277,5278],{"className":26,"code":4982,"language":28,"meta":29,"style":29},[31,5279,5280],{"__ignoreMap":29},[34,5281,5282,5284,5286,5288,5290,5292,5294,5296,5298],{"class":36,"line":37},[34,5283,41],{"class":40},[34,5285,376],{"class":44},[34,5287,4993],{"class":40},[34,5289,2226],{"class":40},[34,5291,382],{"class":44},[34,5293,4783],{"class":40},[34,5295,3945],{"class":44},[34,5297,4801],{"class":40},[34,5299,2278],{"class":44},[12,5301,5302],{},"更加简单。",[146,5304],{},[4301,5306,5307,5323],{},[4304,5308,5309],{},[4307,5310,5311,5314,5318,5320],{},[4310,5312,5313],{},"锁",[4310,5315,5317],{"align":5316},"right","单 mutex 性能",[4310,5319,4505],{"align":5316},[4310,5321,5322],{"align":5316},"多 mutex",[4320,5324,5325,5339,5354],{},[4307,5326,5327,5331,5334,5336],{},[4325,5328,5329],{},[31,5330,2257],{},[4325,5332,5333],{"align":5316},"⭐⭐⭐⭐⭐",[4325,5335,4508],{"align":5316},[4325,5337,5338],{"align":5316},"不方便",[4307,5340,5341,5345,5347,5349],{},[4325,5342,5343],{},[31,5344,4993],{},[4325,5346,5333],{"align":5316},[4325,5348,4508],{"align":5316},[4325,5350,5351],{"align":5316},[16,5352,5353],{},"最好",[4307,5355,5356,5360,5363,5368],{},[4325,5357,5358],{},[31,5359,3664],{},[4325,5361,5362],{"align":5316},"⭐⭐⭐⭐≈",[4325,5364,5365],{"align":5316},[16,5366,5367],{},"最高",[4325,5369,5370,5371],{"align":5316},"可配合 ",[31,5372,5373],{},"std::lock",[149,5375,5377],{"id":5376},"其他-mutex-类型","其他 mutex 类型",[470,5379,5381],{"id":5380},"stdrecursive_mutex",[31,5382,128],{},[12,5384,5385],{},"普通：",[24,5387,5388],{"className":26,"code":27,"language":28,"meta":29,"style":29},[31,5389,5390],{"__ignoreMap":29},[34,5391,5392,5394],{"class":36,"line":37},[34,5393,41],{"class":40},[34,5395,45],{"class":44},[12,5397,5398],{},"不适合让同一个线程重复获得同一把 mutex。",[12,5400,715],{},[24,5402,5404],{"className":26,"code":5403,"language":28,"meta":29,"style":29},"void A()\n{\n    std::lock_guard lock(mutex);\n\n    B();\n}\n\nvoid B()\n{\n    std::lock_guard lock(mutex);\n}\n",[31,5405,5406,5415,5419,5429,5433,5440,5444,5448,5457,5461,5471],{"__ignoreMap":29},[34,5407,5408,5410,5413],{"class":36,"line":37},[34,5409,214],{"class":164},[34,5411,5412],{"class":40}," A",[34,5414,220],{"class":44},[34,5416,5417],{"class":36,"line":172},[34,5418,226],{"class":44},[34,5420,5421,5423,5425,5427],{"class":36,"line":180},[34,5422,315],{"class":40},[34,5424,2738],{"class":44},[34,5426,918],{"class":40},[34,5428,2110],{"class":44},[34,5430,5431],{"class":36,"line":187},[34,5432,184],{"emptyLinePlaceholder":183},[34,5434,5435,5438],{"class":36,"line":206},[34,5436,5437],{"class":40},"    B",[34,5439,353],{"class":44},[34,5441,5442],{"class":36,"line":211},[34,5443,289],{"class":44},[34,5445,5446],{"class":36,"line":223},[34,5447,184],{"emptyLinePlaceholder":183},[34,5449,5450,5452,5455],{"class":36,"line":229},[34,5451,214],{"class":164},[34,5453,5454],{"class":40}," B",[34,5456,220],{"class":44},[34,5458,5459],{"class":36,"line":265},[34,5460,226],{"class":44},[34,5462,5463,5465,5467,5469],{"class":36,"line":271},[34,5464,315],{"class":40},[34,5466,2738],{"class":44},[34,5468,918],{"class":40},[34,5470,2110],{"class":44},[34,5472,5473],{"class":36,"line":280},[34,5474,289],{"class":44},[12,5476,5477],{},"调用：",[24,5479,5482],{"className":5480,"code":5481,"language":404,"meta":29},[402],"A()\n↓\n已经获得 mutex\n↓\nA() 调用 B()\n↓\nB() 又尝试获得同一把 mutex\n",[31,5483,5481],{"__ignoreMap":29},[12,5485,5486],{},"这会出问题。",[12,5488,5489],{},"如果程序确实需要：",[50,5491,5492],{},[12,5493,5494],{},"同一个线程重复进入由同一把锁保护的区域",[12,5496,5497],{},"可以使用：",[24,5499,5501],{"className":26,"code":5500,"language":28,"meta":29,"style":29},"std::recursive_mutex\n",[31,5502,5503],{"__ignoreMap":29},[34,5504,5505,5507],{"class":36,"line":37},[34,5506,41],{"class":40},[34,5508,5509],{"class":44},"::recursive_mutex\n",[12,5511,715],{},[24,5513,5515],{"className":26,"code":5514,"language":28,"meta":29,"style":29},"std::recursive_mutex mutex;\n",[31,5516,5517],{"__ignoreMap":29},[34,5518,5519,5521],{"class":36,"line":37},[34,5520,41],{"class":40},[34,5522,5523],{"class":44},"::recursive_mutex mutex;\n",[12,5525,5526],{},"它允许同一个线程重复获得这把锁。",[12,5528,5529],{},"不过：",[50,5531,5532],{},[12,5533,5534,5537],{},[31,5535,5536],{},"recursive_mutex"," 通常不应该成为第一选择。",[12,5539,5540],{},"如果代码大量依赖递归锁，有时说明：",[24,5542,5545],{"className":5543,"code":5544,"language":404,"meta":29},[402],"函数职责\n或\n锁的边界\n",[31,5546,5544],{"__ignoreMap":29},[12,5548,5549],{},"设计得过于复杂。",[12,5551,5552],{},"能通过重新整理代码结构避免递归加锁时，通常更加容易维护。",[146,5554],{},[470,5556,5558],{"id":5557},"stdtimed_mutex",[31,5559,132],{},[12,5561,5385],{},[24,5563,5564],{"className":26,"code":1062,"language":28,"meta":29,"style":29},[31,5565,5566],{"__ignoreMap":29},[34,5567,5568,5570,5572],{"class":36,"line":37},[34,5569,915],{"class":44},[34,5571,918],{"class":40},[34,5573,353],{"class":44},[12,5575,5576],{},"可以理解成：",[50,5578,5579],{},[12,5580,5581],{},"暂时拿不到，就一直等待。",[12,5583,5584],{},"但有时候程序不希望无限等待。",[12,5586,715],{},[24,5588,5591],{"className":5589,"code":5590,"language":404,"meta":29},[402],"最多等 100ms\n",[31,5592,5590],{"__ignoreMap":29},[12,5594,1247],{},[24,5596,5598],{"className":26,"code":5597,"language":28,"meta":29,"style":29},"std::timed_mutex\n",[31,5599,5600],{"__ignoreMap":29},[34,5601,5602,5604],{"class":36,"line":37},[34,5603,41],{"class":40},[34,5605,5606],{"class":44},"::timed_mutex\n",[12,5608,5609],{},"它支持：",[24,5611,5613],{"className":26,"code":5612,"language":28,"meta":29,"style":29},"try_lock_for()\ntry_lock_until()\n",[31,5614,5615,5622],{"__ignoreMap":29},[34,5616,5617,5620],{"class":36,"line":37},[34,5618,5619],{"class":40},"try_lock_for",[34,5621,220],{"class":44},[34,5623,5624,5627],{"class":36,"line":172},[34,5625,5626],{"class":40},"try_lock_until",[34,5628,220],{"class":44},[12,5630,715],{},[24,5632,5634],{"className":26,"code":5633,"language":28,"meta":29,"style":29},"#include \u003Cchrono>\n#include \u003Cmutex>\n\nstd::timed_mutex mutex;\n\nif (mutex.try_lock_for(std::chrono::milliseconds(100)))\n{\n    \u002F\u002F 成功获得 mutex\n\n    mutex.unlock();\n}\nelse\n{\n    \u002F\u002F 在等待时间内没有获得 mutex\n}\n",[31,5635,5636,5643,5649,5653,5660,5664,5694,5698,5703,5707,5715,5719,5723,5727,5732],{"__ignoreMap":29},[34,5637,5638,5640],{"class":36,"line":37},[34,5639,165],{"class":164},[34,5641,5642],{"class":168}," \u003Cchrono>\n",[34,5644,5645,5647],{"class":36,"line":172},[34,5646,165],{"class":164},[34,5648,1014],{"class":168},[34,5650,5651],{"class":36,"line":180},[34,5652,184],{"emptyLinePlaceholder":183},[34,5654,5655,5657],{"class":36,"line":187},[34,5656,41],{"class":40},[34,5658,5659],{"class":44},"::timed_mutex mutex;\n",[34,5661,5662],{"class":36,"line":206},[34,5663,184],{"emptyLinePlaceholder":183},[34,5665,5666,5668,5670,5672,5674,5676,5678,5681,5683,5686,5688,5691],{"class":36,"line":211},[34,5667,1270],{"class":164},[34,5669,1273],{"class":44},[34,5671,5619],{"class":40},[34,5673,382],{"class":44},[34,5675,41],{"class":40},[34,5677,376],{"class":44},[34,5679,5680],{"class":40},"chrono",[34,5682,376],{"class":44},[34,5684,5685],{"class":40},"milliseconds",[34,5687,382],{"class":44},[34,5689,5690],{"class":199},"100",[34,5692,5693],{"class":44},")))\n",[34,5695,5696],{"class":36,"line":223},[34,5697,226],{"class":44},[34,5699,5700],{"class":36,"line":229},[34,5701,5702],{"class":1320},"    \u002F\u002F 成功获得 mutex\n",[34,5704,5705],{"class":36,"line":265},[34,5706,184],{"emptyLinePlaceholder":183},[34,5708,5709,5711,5713],{"class":36,"line":271},[34,5710,1298],{"class":44},[34,5712,933],{"class":40},[34,5714,353],{"class":44},[34,5716,5717],{"class":36,"line":280},[34,5718,289],{"class":44},[34,5720,5721],{"class":36,"line":286},[34,5722,1311],{"class":164},[34,5724,5725],{"class":36,"line":292},[34,5726,226],{"class":44},[34,5728,5729],{"class":36,"line":297},[34,5730,5731],{"class":1320},"    \u002F\u002F 在等待时间内没有获得 mutex\n",[34,5733,5734],{"class":36,"line":307},[34,5735,289],{"class":44},[12,5737,5738],{},"其中：",[24,5740,5742],{"className":26,"code":5741,"language":28,"meta":29,"style":29},"try_lock_for()\n",[31,5743,5744],{"__ignoreMap":29},[34,5745,5746,5748],{"class":36,"line":37},[34,5747,5619],{"class":40},[34,5749,220],{"class":44},[12,5751,5752],{},"强调：",[50,5754,5755],{},[12,5756,5757],{},"最多等待多长时间。",[12,5759,715],{},[24,5761,5764],{"className":5762,"code":5763,"language":404,"meta":29},[402],"100ms\n2s\n",[31,5765,5763],{"__ignoreMap":29},[12,5767,1337],{},[24,5769,5771],{"className":26,"code":5770,"language":28,"meta":29,"style":29},"try_lock_until()\n",[31,5772,5773],{"__ignoreMap":29},[34,5774,5775,5777],{"class":36,"line":37},[34,5776,5626],{"class":40},[34,5778,220],{"class":44},[12,5780,5752],{},[50,5782,5783],{},[12,5784,5785],{},"等待到哪个时间点。",[12,5787,5788],{},"它们和之前学习过的：",[24,5790,5792],{"className":26,"code":5791,"language":28,"meta":29,"style":29},"sleep_for()\nsleep_until()\n",[31,5793,5794,5801],{"__ignoreMap":29},[34,5795,5796,5799],{"class":36,"line":37},[34,5797,5798],{"class":40},"sleep_for",[34,5800,220],{"class":44},[34,5802,5803,5806],{"class":36,"line":172},[34,5804,5805],{"class":40},"sleep_until",[34,5807,220],{"class":44},[12,5809,5810],{},"在“相对时间 \u002F 绝对时间”这个思路上比较类似。",[146,5812],{},[470,5814,5816,5818],{"id":5815},"stdshared_mutex多读单写",[31,5817,137],{},"：多读单写",[12,5820,5821],{},"普通 mutex 有一个特点：",[50,5823,5824],{},[12,5825,5826],{},"不管你是读数据还是写数据，同一时刻都只能有一个线程持有锁。",[12,5828,5829],{},"假设程序里有：",[24,5831,5833],{"className":26,"code":5832,"language":28,"meta":29,"style":29},"std::string config;\n",[31,5834,5835],{"__ignoreMap":29},[34,5836,5837,5839],{"class":36,"line":37},[34,5838,41],{"class":40},[34,5840,5841],{"class":44},"::string config;\n",[12,5843,5844],{},"很多线程都只是：",[24,5846,5849],{"className":5847,"code":5848,"language":404,"meta":29},[402],"读取 config\n",[31,5850,5848],{"__ignoreMap":29},[12,5852,5853],{},"真正修改它的情况非常少。",[12,5855,5856],{},"如果使用普通 mutex：",[24,5858,5861],{"className":5859,"code":5860,"language":404,"meta":29},[402],"线程 A 读\n↓\n线程 B 即使也只是读\n也必须等待\n",[31,5862,5860],{"__ignoreMap":29},[12,5864,2884],{},[24,5866,5869],{"className":5867,"code":5868,"language":404,"meta":29},[402],"读 + 读\n",[31,5870,5868],{"__ignoreMap":29},[12,5872,5873],{},"通常并不会互相破坏数据。",[12,5875,5876],{},"真正危险的是：",[24,5878,5881],{"className":5879,"code":5880,"language":404,"meta":29},[402],"读 + 写\n",[31,5882,5880],{"__ignoreMap":29},[12,5884,5885],{},"或者：",[24,5887,5890],{"className":5888,"code":5889,"language":404,"meta":29},[402],"写 + 写\n",[31,5891,5889],{"__ignoreMap":29},[12,5893,5894],{},"所以 C++17 提供：",[24,5896,5898],{"className":26,"code":5897,"language":28,"meta":29,"style":29},"std::shared_mutex\n",[31,5899,5900],{"__ignoreMap":29},[34,5901,5902,5904],{"class":36,"line":37},[34,5903,41],{"class":40},[34,5905,5906],{"class":44},"::shared_mutex\n",[12,5908,5909],{},"它可以支持：",[50,5911,5912],{},[12,5913,5914],{},"多个读线程同时进入，但写线程必须独占。",[146,5916],{},[1051,5918,5920,129,5923],{"id":5919},"shared_lock-和-unique_lock",[31,5921,5922],{},"shared_lock",[31,5924,3664],{},[12,5926,5927,5928,5931],{},"使用 ",[31,5929,5930],{},"shared_mutex"," 时：",[12,5933,5934],{},"读取通常使用：",[24,5936,5938],{"className":26,"code":5937,"language":28,"meta":29,"style":29},"std::shared_lock\n",[31,5939,5940],{"__ignoreMap":29},[34,5941,5942,5944],{"class":36,"line":37},[34,5943,41],{"class":40},[34,5945,5946],{"class":44},"::shared_lock\n",[12,5948,5949],{},"写入通常使用：",[24,5951,5952],{"className":26,"code":3638,"language":28,"meta":29,"style":29},[31,5953,5954],{"__ignoreMap":29},[34,5955,5956,5958],{"class":36,"line":37},[34,5957,41],{"class":40},[34,5959,3647],{"class":44},[12,5961,715],{},[24,5963,5965],{"className":26,"code":5964,"language":28,"meta":29,"style":29},"#include \u003Cmutex>\n#include \u003Cshared_mutex>\n#include \u003Cstring>\n\nstd::string config;\nstd::shared_mutex config_mutex;\n\nstd::string read_config()\n{\n    std::shared_lock lock(config_mutex);\n\n    return config;\n}\n\nvoid write_config(std::string value)\n{\n    std::unique_lock lock(config_mutex);\n\n    config = std::move(value);\n}\n",[31,5966,5967,5973,5980,5987,5991,5997,6004,6008,6022,6026,6038,6042,6049,6053,6057,6079,6083,6094,6098,6115],{"__ignoreMap":29},[34,5968,5969,5971],{"class":36,"line":37},[34,5970,165],{"class":164},[34,5972,1014],{"class":168},[34,5974,5975,5977],{"class":36,"line":172},[34,5976,165],{"class":164},[34,5978,5979],{"class":168}," \u003Cshared_mutex>\n",[34,5981,5982,5984],{"class":36,"line":180},[34,5983,165],{"class":164},[34,5985,5986],{"class":168}," \u003Cstring>\n",[34,5988,5989],{"class":36,"line":187},[34,5990,184],{"emptyLinePlaceholder":183},[34,5992,5993,5995],{"class":36,"line":206},[34,5994,41],{"class":40},[34,5996,5841],{"class":44},[34,5998,5999,6001],{"class":36,"line":211},[34,6000,41],{"class":40},[34,6002,6003],{"class":44},"::shared_mutex config_mutex;\n",[34,6005,6006],{"class":36,"line":223},[34,6007,184],{"emptyLinePlaceholder":183},[34,6009,6010,6012,6014,6017,6020],{"class":36,"line":229},[34,6011,41],{"class":40},[34,6013,376],{"class":44},[34,6015,6016],{"class":40},"string",[34,6018,6019],{"class":40}," read_config",[34,6021,220],{"class":44},[34,6023,6024],{"class":36,"line":265},[34,6025,226],{"class":44},[34,6027,6028,6030,6033,6035],{"class":36,"line":271},[34,6029,315],{"class":40},[34,6031,6032],{"class":44},"::shared_lock ",[34,6034,918],{"class":40},[34,6036,6037],{"class":44},"(config_mutex);\n",[34,6039,6040],{"class":36,"line":280},[34,6041,184],{"emptyLinePlaceholder":183},[34,6043,6044,6046],{"class":36,"line":286},[34,6045,1845],{"class":164},[34,6047,6048],{"class":44}," config;\n",[34,6050,6051],{"class":36,"line":292},[34,6052,289],{"class":44},[34,6054,6055],{"class":36,"line":297},[34,6056,184],{"emptyLinePlaceholder":183},[34,6058,6059,6061,6064,6066,6068,6070,6072,6076],{"class":36,"line":307},[34,6060,214],{"class":164},[34,6062,6063],{"class":40}," write_config",[34,6065,382],{"class":44},[34,6067,41],{"class":40},[34,6069,376],{"class":44},[34,6071,6016],{"class":40},[34,6073,6075],{"class":6074},"s4XuR"," value",[34,6077,6078],{"class":44},")\n",[34,6080,6081],{"class":36,"line":312},[34,6082,226],{"class":44},[34,6084,6085,6087,6090,6092],{"class":36,"line":327},[34,6086,315],{"class":40},[34,6088,6089],{"class":44},"::unique_lock ",[34,6091,918],{"class":40},[34,6093,6037],{"class":44},[34,6095,6096],{"class":36,"line":339},[34,6097,184],{"emptyLinePlaceholder":183},[34,6099,6100,6103,6105,6108,6110,6112],{"class":36,"line":344},[34,6101,6102],{"class":44},"    config ",[34,6104,196],{"class":164},[34,6106,6107],{"class":40}," std",[34,6109,376],{"class":44},[34,6111,4461],{"class":40},[34,6113,6114],{"class":44},"(value);\n",[34,6116,6117],{"class":36,"line":356},[34,6118,289],{"class":44},[12,6120,6121],{},"多个线程都读取：",[24,6123,6126],{"className":6124,"code":6125,"language":404,"meta":29},[402],"线程 A：shared_lock\n线程 B：shared_lock\n线程 C：shared_lock\n",[31,6127,6125],{"__ignoreMap":29},[12,6129,6130],{},"可以同时存在。",[12,6132,6133],{},"但是写线程：",[24,6135,6137],{"className":26,"code":6136,"language":28,"meta":29,"style":29},"std::unique_lock lock(config_mutex);\n",[31,6138,6139],{"__ignoreMap":29},[34,6140,6141,6143,6145,6147,6149,6151,6154],{"class":36,"line":37},[34,6142,41],{"class":40},[34,6144,376],{"class":44},[34,6146,3664],{"class":40},[34,6148,2226],{"class":40},[34,6150,382],{"class":44},[34,6152,6153],{"class":40},"config_mutex",[34,6155,2278],{"class":44},[12,6157,6158],{},"必须独占。",[12,6160,1126],{},[24,6162,6165],{"className":6163,"code":6164,"language":404,"meta":29},[402],"shared_lock + shared_lock\n✓ 可以同时存在\n\nshared_lock + unique_lock\n✗ 不可以\n\nunique_lock + unique_lock\n✗ 不可以\n",[31,6166,6164],{"__ignoreMap":29},[12,6168,2149],{},[50,6170,6171],{},[12,6172,6173],{},"多读单写。",[146,6175],{},[1051,6177,6178],{"id":6178},"为什么读取也可能需要锁",[12,6180,6181],{},"有一种很常见的错误写法：",[24,6183,6185],{"className":26,"code":6184,"language":28,"meta":29,"style":29},"void write()\n{\n    std::lock_guard lock(mutex);\n\n    value = 10;\n}\n\nint read()\n{\n    return value;\n}\n",[31,6186,6187,6196,6200,6210,6214,6226,6230,6234,6243,6247,6254],{"__ignoreMap":29},[34,6188,6189,6191,6194],{"class":36,"line":37},[34,6190,214],{"class":164},[34,6192,6193],{"class":40}," write",[34,6195,220],{"class":44},[34,6197,6198],{"class":36,"line":172},[34,6199,226],{"class":44},[34,6201,6202,6204,6206,6208],{"class":36,"line":180},[34,6203,315],{"class":40},[34,6205,2738],{"class":44},[34,6207,918],{"class":40},[34,6209,2110],{"class":44},[34,6211,6212],{"class":36,"line":187},[34,6213,184],{"emptyLinePlaceholder":183},[34,6215,6216,6219,6221,6224],{"class":36,"line":206},[34,6217,6218],{"class":44},"    value ",[34,6220,196],{"class":164},[34,6222,6223],{"class":199}," 10",[34,6225,203],{"class":44},[34,6227,6228],{"class":36,"line":211},[34,6229,289],{"class":44},[34,6231,6232],{"class":36,"line":223},[34,6233,184],{"emptyLinePlaceholder":183},[34,6235,6236,6238,6241],{"class":36,"line":229},[34,6237,190],{"class":164},[34,6239,6240],{"class":40}," read",[34,6242,220],{"class":44},[34,6244,6245],{"class":36,"line":265},[34,6246,226],{"class":44},[34,6248,6249,6251],{"class":36,"line":271},[34,6250,1845],{"class":164},[34,6252,6253],{"class":44}," value;\n",[34,6255,6256],{"class":36,"line":280},[34,6257,289],{"class":44},[12,6259,6260],{},"有人会觉得：",[50,6262,6263],{},[12,6264,6265],{},"写的时候加锁就够了，读又不会修改变量。",[12,6267,6268],{},"实际上并不是。",[12,6270,578],{},[24,6272,6275],{"className":6273,"code":6274,"language":404,"meta":29},[402],"线程 A 正在写 value\n",[31,6276,6274],{"__ignoreMap":29},[12,6278,6279],{},"同时：",[24,6281,6284],{"className":6282,"code":6283,"language":404,"meta":29},[402],"线程 B 正在读 value\n",[31,6285,6283],{"__ignoreMap":29},[12,6287,6288],{},"仍然可能产生数据竞争。",[12,6290,1112],{},[50,6292,6293],{},[12,6294,6295],{},"如果共享数据存在并发写入，那么对应的读取通常也必须参与同一套同步规则。",[12,6297,6298],{},"不能只锁写，不管读。",[146,6300],{},[1051,6302,6304,6306],{"id":6303},"shared_mutex-不一定比普通-mutex-更快",[31,6305,5930],{}," 不一定比普通 mutex 更快",[12,6308,6309],{},"看到：",[24,6311,6314],{"className":6312,"code":6313,"language":404,"meta":29},[402],"多个线程可以同时读\n",[31,6315,6313],{"__ignoreMap":29},[12,6317,6318],{},"很容易产生一个误解：",[50,6320,6321],{},[12,6322,6323],{},"那以后全部用 shared_mutex 不就好了？",[12,6325,6326,6327,6329],{},"实际上 ",[31,6328,5930],{}," 自己需要维护更加复杂的状态，例如：",[24,6331,6334],{"className":6332,"code":6333,"language":404,"meta":29},[402],"现在有几个读线程？\n有没有写线程？\n什么时候允许写线程进入？\n",[31,6335,6333],{"__ignoreMap":29},[12,6337,6338],{},"这些本身都有额外开销。",[12,6340,1112],{},[24,6342,6343],{"className":26,"code":5897,"language":28,"meta":29,"style":29},[31,6344,6345],{"__ignoreMap":29},[34,6346,6347,6349],{"class":36,"line":37},[34,6348,41],{"class":40},[34,6350,5906],{"class":44},[12,6352,6353],{},"比较适合：",[24,6355,6358],{"className":6356,"code":6357,"language":404,"meta":29},[402],"读很多\n写很少\n并且确实存在大量并发读取\n",[31,6359,6357],{"__ignoreMap":29},[12,6361,6362],{},"的场景。",[12,6364,6365],{},"不要简单认为：",[24,6367,6370],{"className":6368,"code":6369,"language":404,"meta":29},[402],"shared_mutex 一定比 mutex 快\n",[31,6371,6369],{"__ignoreMap":29},[12,6373,6374],{},"它们只是适用场景不同。",[146,6376],{},[149,6378,6379],{"id":6379},"一次性初始化",[470,6381,6383],{"id":6382},"stdcall_once",[31,6384,143],{},[12,6386,6387],{},"还有一类特殊的多线程需求：",[50,6389,6390],{},[12,6391,6392],{},"某段初始化代码只能成功执行一次。",[12,6394,715],{},[24,6396,6398],{"className":26,"code":6397,"language":28,"meta":29,"style":29},"initialize();\n",[31,6399,6400],{"__ignoreMap":29},[34,6401,6402,6405],{"class":36,"line":37},[34,6403,6404],{"class":40},"initialize",[34,6406,353],{"class":44},[12,6408,6409],{},"可能有三个线程同时第一次进入：",[24,6411,6414],{"className":6412,"code":6413,"language":404,"meta":29},[402],"线程 A\n线程 B\n线程 C\n",[31,6415,6413],{"__ignoreMap":29},[12,6417,6418],{},"但我们希望：",[24,6420,6423],{"className":6421,"code":6422,"language":404,"meta":29},[402],"initialize()\n",[31,6424,6422],{"__ignoreMap":29},[12,6426,6427],{},"最终只成功完成一次。",[12,6429,6430],{},"标准库提供：",[24,6432,6434],{"className":26,"code":6433,"language":28,"meta":29,"style":29},"std::once_flag\nstd::call_once\n",[31,6435,6436,6443],{"__ignoreMap":29},[34,6437,6438,6440],{"class":36,"line":37},[34,6439,41],{"class":40},[34,6441,6442],{"class":44},"::once_flag\n",[34,6444,6445,6447],{"class":36,"line":172},[34,6446,41],{"class":40},[34,6448,6449],{"class":44},"::call_once\n",[12,6451,715],{},[24,6453,6455],{"className":26,"code":6454,"language":28,"meta":29,"style":29},"#include \u003Cprint>\n#include \u003Cmutex>\n#include \u003Cthread>\n\nstd::once_flag flag;\n\nvoid initialize()\n{\n    std::println(\"initialize once\");\n}\n\nvoid worker()\n{\n    std::call_once(flag, initialize);\n}\n\nint main()\n{\n    std::thread t1(worker);\n    std::thread t2(worker);\n    std::thread t3(worker);\n\n    t1.join();\n    t2.join();\n    t3.join();\n}\n",[31,6456,6457,6463,6469,6475,6479,6486,6490,6499,6503,6518,6522,6526,6535,6539,6551,6555,6559,6567,6571,6582,6592,6603,6607,6615,6623,6632],{"__ignoreMap":29},[34,6458,6459,6461],{"class":36,"line":37},[34,6460,165],{"class":164},[34,6462,169],{"class":168},[34,6464,6465,6467],{"class":36,"line":172},[34,6466,165],{"class":164},[34,6468,1014],{"class":168},[34,6470,6471,6473],{"class":36,"line":180},[34,6472,165],{"class":164},[34,6474,177],{"class":168},[34,6476,6477],{"class":36,"line":187},[34,6478,184],{"emptyLinePlaceholder":183},[34,6480,6481,6483],{"class":36,"line":206},[34,6482,41],{"class":40},[34,6484,6485],{"class":44},"::once_flag flag;\n",[34,6487,6488],{"class":36,"line":211},[34,6489,184],{"emptyLinePlaceholder":183},[34,6491,6492,6494,6497],{"class":36,"line":223},[34,6493,214],{"class":164},[34,6495,6496],{"class":40}," initialize",[34,6498,220],{"class":44},[34,6500,6501],{"class":36,"line":229},[34,6502,226],{"class":44},[34,6504,6505,6507,6509,6511,6513,6516],{"class":36,"line":265},[34,6506,315],{"class":40},[34,6508,376],{"class":44},[34,6510,379],{"class":40},[34,6512,382],{"class":44},[34,6514,6515],{"class":168},"\"initialize once\"",[34,6517,2278],{"class":44},[34,6519,6520],{"class":36,"line":271},[34,6521,289],{"class":44},[34,6523,6524],{"class":36,"line":280},[34,6525,184],{"emptyLinePlaceholder":183},[34,6527,6528,6530,6533],{"class":36,"line":286},[34,6529,214],{"class":164},[34,6531,6532],{"class":40}," worker",[34,6534,220],{"class":44},[34,6536,6537],{"class":36,"line":292},[34,6538,226],{"class":44},[34,6540,6541,6543,6545,6548],{"class":36,"line":297},[34,6542,315],{"class":40},[34,6544,376],{"class":44},[34,6546,6547],{"class":40},"call_once",[34,6549,6550],{"class":44},"(flag, initialize);\n",[34,6552,6553],{"class":36,"line":307},[34,6554,289],{"class":44},[34,6556,6557],{"class":36,"line":312},[34,6558,184],{"emptyLinePlaceholder":183},[34,6560,6561,6563,6565],{"class":36,"line":327},[34,6562,190],{"class":164},[34,6564,302],{"class":40},[34,6566,220],{"class":44},[34,6568,6569],{"class":36,"line":339},[34,6570,226],{"class":44},[34,6572,6573,6575,6577,6579],{"class":36,"line":344},[34,6574,315],{"class":40},[34,6576,318],{"class":44},[34,6578,321],{"class":40},[34,6580,6581],{"class":44},"(worker);\n",[34,6583,6584,6586,6588,6590],{"class":36,"line":356},[34,6585,315],{"class":40},[34,6587,318],{"class":44},[34,6589,334],{"class":40},[34,6591,6581],{"class":44},[34,6593,6594,6596,6598,6601],{"class":36,"line":366},[34,6595,315],{"class":40},[34,6597,318],{"class":44},[34,6599,6600],{"class":40},"t3",[34,6602,6581],{"class":44},[34,6604,6605],{"class":36,"line":371},[34,6606,184],{"emptyLinePlaceholder":183},[34,6608,6609,6611,6613],{"class":36,"line":391},[34,6610,347],{"class":44},[34,6612,350],{"class":40},[34,6614,353],{"class":44},[34,6616,6617,6619,6621],{"class":36,"line":1620},[34,6618,359],{"class":44},[34,6620,350],{"class":40},[34,6622,353],{"class":44},[34,6624,6625,6628,6630],{"class":36,"line":1625},[34,6626,6627],{"class":44},"    t3.",[34,6629,350],{"class":40},[34,6631,353],{"class":44},[34,6633,6634],{"class":36,"line":1634},[34,6635,289],{"class":44},[12,6637,6638],{},[16,6639,1670],{},[24,6641,6644],{"className":6642,"code":6643,"language":404,"meta":29},[402],"initialize once\n",[31,6645,6643],{"__ignoreMap":29},[12,6647,6648],{},"三个线程都会执行：",[24,6650,6652],{"className":26,"code":6651,"language":28,"meta":29,"style":29},"std::call_once(flag, initialize);\n",[31,6653,6654],{"__ignoreMap":29},[34,6655,6656,6658,6660,6662],{"class":36,"line":37},[34,6657,41],{"class":40},[34,6659,376],{"class":44},[34,6661,6547],{"class":40},[34,6663,6550],{"class":44},[12,6665,2884],{},[24,6667,6668],{"className":26,"code":6397,"language":28,"meta":29,"style":29},[31,6669,6670],{"__ignoreMap":29},[34,6671,6672,6674],{"class":36,"line":37},[34,6673,6404],{"class":40},[34,6675,353],{"class":44},[12,6677,6678],{},"只会成功完成一次。",[12,6680,6681],{},"可以粗略理解成：",[24,6683,6686],{"className":6684,"code":6685,"language":404,"meta":29},[402],"线程 A ─┐\n线程 B ─┼→ call_once → initialize 成功一次\n线程 C ─┘\n",[31,6687,6685],{"__ignoreMap":29},[12,6689,6690],{},"如果初始化函数执行过程中抛出异常，这一次不会被认为已经成功完成，后续调用仍然可以再次尝试。",[146,6692],{},[470,6694,6696,6697,6700],{"id":6695},"函数局部-static-初始化也是线程安全的","函数局部 ",[31,6698,6699],{},"static"," 初始化也是线程安全的",[12,6702,6703],{},"C++11 开始：",[24,6705,6707],{"className":26,"code":6706,"language":28,"meta":29,"style":29},"MyObject& instance()\n{\n    static MyObject object;\n\n    return object;\n}\n",[31,6708,6709,6722,6726,6734,6738,6745],{"__ignoreMap":29},[34,6710,6711,6714,6717,6720],{"class":36,"line":37},[34,6712,6713],{"class":40},"MyObject",[34,6715,6716],{"class":164},"&",[34,6718,6719],{"class":40}," instance",[34,6721,220],{"class":44},[34,6723,6724],{"class":36,"line":172},[34,6725,226],{"class":44},[34,6727,6728,6731],{"class":36,"line":180},[34,6729,6730],{"class":164},"    static",[34,6732,6733],{"class":44}," MyObject object;\n",[34,6735,6736],{"class":36,"line":187},[34,6737,184],{"emptyLinePlaceholder":183},[34,6739,6740,6742],{"class":36,"line":206},[34,6741,1845],{"class":164},[34,6743,6744],{"class":44}," object;\n",[34,6746,6747],{"class":36,"line":211},[34,6748,289],{"class":44},[12,6750,3555],{},[24,6752,6754],{"className":26,"code":6753,"language":28,"meta":29,"style":29},"static MyObject object;\n",[31,6755,6756],{"__ignoreMap":29},[34,6757,6758,6760],{"class":36,"line":37},[34,6759,6699],{"class":164},[34,6761,6733],{"class":44},[12,6763,6764],{},"的初始化本身已经具有线程安全保证。",[12,6766,6767],{},"所以如果目的只是：",[50,6769,6770],{},[12,6771,6772],{},"延迟初始化一个函数内部的 static 对象",[12,6774,6775],{},"通常不需要额外再写：",[24,6777,6779],{"className":26,"code":6778,"language":28,"meta":29,"style":29},"std::call_once\n",[31,6780,6781],{"__ignoreMap":29},[34,6782,6783,6785],{"class":36,"line":37},[34,6784,41],{"class":40},[34,6786,6449],{"class":44},[146,6788],{},[149,6790,6792],{"id":6791},"工程实践-重要","工程实践 （重要）",[470,6794,6796],{"id":6795},"最好把-mutex-和它保护的数据封装在一起","最好把 mutex 和它保护的数据封装在一起",[12,6798,6799],{},"不推荐设计成：",[24,6801,6804],{"className":6802,"code":6803,"language":404,"meta":29},[402],"函数 A：\n调用前必须先锁 mutex\n\n函数 B：\n自己内部会锁\n\n函数 C：\n只能在已经持锁时调用\n\n函数 D：\n有时候锁，有时候不锁\n",[31,6805,6803],{"__ignoreMap":29},[12,6807,6808],{},"代码一多以后，很容易变成：",[50,6810,6811],{},[12,6812,6813],{},"到底谁负责加锁？",[12,6815,6816],{},"更好的设计通常是：",[50,6818,6819],{},[12,6820,6821],{},"让拥有共享数据的类自己管理同步。",[12,6823,715],{},[24,6825,6827],{"className":26,"code":6826,"language":28,"meta":29,"style":29},"class Counter\n{\npublic:\n    void increment()\n    {\n        std::lock_guard lock(mutex_);\n\n        ++value_;\n    }\n\n    int get() const\n    {\n        std::lock_guard lock(mutex_);\n\n        return value_;\n    }\n\nprivate:\n    mutable std::mutex mutex_;\n    int value_ = 0;\n};\n",[31,6828,6829,6837,6841,6846,6856,6860,6871,6875,6882,6886,6890,6902,6906,6916,6920,6928,6932,6936,6941,6951,6964],{"__ignoreMap":29},[34,6830,6831,6834],{"class":36,"line":37},[34,6832,6833],{"class":164},"class",[34,6835,6836],{"class":40}," Counter\n",[34,6838,6839],{"class":36,"line":172},[34,6840,226],{"class":44},[34,6842,6843],{"class":36,"line":180},[34,6844,6845],{"class":164},"public:\n",[34,6847,6848,6851,6854],{"class":36,"line":187},[34,6849,6850],{"class":164},"    void",[34,6852,6853],{"class":40}," increment",[34,6855,220],{"class":44},[34,6857,6858],{"class":36,"line":206},[34,6859,268],{"class":44},[34,6861,6862,6864,6866,6868],{"class":36,"line":211},[34,6863,2479],{"class":40},[34,6865,2738],{"class":44},[34,6867,918],{"class":40},[34,6869,6870],{"class":44},"(mutex_);\n",[34,6872,6873],{"class":36,"line":223},[34,6874,184],{"emptyLinePlaceholder":183},[34,6876,6877,6879],{"class":36,"line":229},[34,6878,274],{"class":164},[34,6880,6881],{"class":44},"value_;\n",[34,6883,6884],{"class":36,"line":265},[34,6885,283],{"class":44},[34,6887,6888],{"class":36,"line":271},[34,6889,184],{"emptyLinePlaceholder":183},[34,6891,6892,6894,6897,6899],{"class":36,"line":280},[34,6893,3233],{"class":164},[34,6895,6896],{"class":40}," get",[34,6898,1418],{"class":44},[34,6900,6901],{"class":164},"const\n",[34,6903,6904],{"class":36,"line":286},[34,6905,268],{"class":44},[34,6907,6908,6910,6912,6914],{"class":36,"line":292},[34,6909,2479],{"class":40},[34,6911,2738],{"class":44},[34,6913,918],{"class":40},[34,6915,6870],{"class":44},[34,6917,6918],{"class":36,"line":297},[34,6919,184],{"emptyLinePlaceholder":183},[34,6921,6922,6925],{"class":36,"line":307},[34,6923,6924],{"class":164},"        return",[34,6926,6927],{"class":44}," value_;\n",[34,6929,6930],{"class":36,"line":312},[34,6931,283],{"class":44},[34,6933,6934],{"class":36,"line":327},[34,6935,184],{"emptyLinePlaceholder":183},[34,6937,6938],{"class":36,"line":339},[34,6939,6940],{"class":164},"private:\n",[34,6942,6943,6946,6948],{"class":36,"line":344},[34,6944,6945],{"class":164},"    mutable",[34,6947,6107],{"class":40},[34,6949,6950],{"class":44},"::mutex mutex_;\n",[34,6952,6953,6955,6958,6960,6962],{"class":36,"line":356},[34,6954,3233],{"class":164},[34,6956,6957],{"class":44}," value_ ",[34,6959,196],{"class":164},[34,6961,200],{"class":199},[34,6963,203],{"class":44},[34,6965,6966],{"class":36,"line":366},[34,6967,3241],{"class":44},[12,6969,6970],{},"外部只需要：",[24,6972,6974],{"className":26,"code":6973,"language":28,"meta":29,"style":29},"counter.increment();\n",[31,6975,6976],{"__ignoreMap":29},[34,6977,6978,6981,6984],{"class":36,"line":37},[34,6979,6980],{"class":44},"counter.",[34,6982,6983],{"class":40},"increment",[34,6985,353],{"class":44},[12,6987,6988],{},"并不需要知道：",[24,6990,6993],{"className":6991,"code":6992,"language":404,"meta":29},[402],"内部用了哪把 mutex\n什么时候 lock\n什么时候 unlock\n",[31,6994,6992],{"__ignoreMap":29},[12,6996,6997],{},"这样同步规则更加集中，也更不容易漏锁。",[146,6999],{},[470,7001,7003,7004,7007],{"id":7002},"为什么-mutex-经常写成-mutable-重要","为什么 mutex 经常写成 ",[31,7005,7006],{},"mutable"," （重要）",[12,7009,600],{},[24,7011,7013],{"className":26,"code":7012,"language":28,"meta":29,"style":29},"int get() const\n",[31,7014,7015],{"__ignoreMap":29},[34,7016,7017,7019,7021,7023],{"class":36,"line":37},[34,7018,190],{"class":164},[34,7020,6896],{"class":40},[34,7022,1418],{"class":44},[34,7024,6901],{"class":164},[12,7026,7027],{},"是一个 const 成员函数。",[12,7029,7030],{},"但是对 mutex：",[24,7032,7034],{"className":26,"code":7033,"language":28,"meta":29,"style":29},"mutex_.lock();\nmutex_.unlock();\n",[31,7035,7036,7045],{"__ignoreMap":29},[34,7037,7038,7041,7043],{"class":36,"line":37},[34,7039,7040],{"class":44},"mutex_.",[34,7042,918],{"class":40},[34,7044,353],{"class":44},[34,7046,7047,7049,7051],{"class":36,"line":172},[34,7048,7040],{"class":44},[34,7050,933],{"class":40},[34,7052,353],{"class":44},[12,7054,7055],{},"会修改 mutex 自己的内部状态。",[12,7057,7058],{},"所以通常写：",[24,7060,7062],{"className":26,"code":7061,"language":28,"meta":29,"style":29},"mutable std::mutex mutex_;\n",[31,7063,7064],{"__ignoreMap":29},[34,7065,7066,7068,7070],{"class":36,"line":37},[34,7067,7006],{"class":164},[34,7069,6107],{"class":40},[34,7071,6950],{"class":44},[12,7073,7074],{},"这样即使当前成员函数是：",[24,7076,7077],{"className":26,"code":6901,"language":28,"meta":29,"style":29},[31,7078,7079],{"__ignoreMap":29},[34,7080,7081],{"class":36,"line":37},[34,7082,6901],{"class":164},[12,7084,7085],{},"仍然可以对 mutex 加锁。",[12,7087,7088],{},"这并不意味着：",[24,7090,7092],{"className":26,"code":7091,"language":28,"meta":29,"style":29},"get()\n",[31,7093,7094],{"__ignoreMap":29},[34,7095,7096,7099],{"class":36,"line":37},[34,7097,7098],{"class":40},"get",[34,7100,220],{"class":44},[12,7102,7103],{},"真的修改了 Counter 对外表现出来的逻辑状态。",[12,7105,7106],{},"它只是修改了内部的同步状态。",[12,7108,7109],{},"所以 mutex 写成：",[24,7111,7113],{"className":26,"code":7112,"language":28,"meta":29,"style":29},"mutable\n",[31,7114,7115],{"__ignoreMap":29},[34,7116,7117],{"class":36,"line":37},[34,7118,7112],{"class":164},[12,7120,7121],{},"是非常常见的做法。",[146,7123],{},[470,7125,7126],{"id":7126},"持锁时不要随便调用未知代码",[12,7128,715],{},[24,7130,7132],{"className":26,"code":7131,"language":28,"meta":29,"style":29},"std::lock_guard lock(mutex);\n\nsome_callback();\n",[31,7133,7134,7150,7154],{"__ignoreMap":29},[34,7135,7136,7138,7140,7142,7144,7146,7148],{"class":36,"line":37},[34,7137,41],{"class":40},[34,7139,376],{"class":44},[34,7141,2257],{"class":40},[34,7143,2226],{"class":40},[34,7145,382],{"class":44},[34,7147,2266],{"class":40},[34,7149,2278],{"class":44},[34,7151,7152],{"class":36,"line":172},[34,7153,184],{"emptyLinePlaceholder":183},[34,7155,7156,7159],{"class":36,"line":180},[34,7157,7158],{"class":40},"some_callback",[34,7160,353],{"class":44},[12,7162,578],{},[24,7164,7166],{"className":26,"code":7165,"language":28,"meta":29,"style":29},"some_callback();\n",[31,7167,7168],{"__ignoreMap":29},[34,7169,7170,7172],{"class":36,"line":37},[34,7171,7158],{"class":40},[34,7173,353],{"class":44},[12,7175,7176],{},"内部又尝试获得：",[24,7178,7180],{"className":26,"code":7179,"language":28,"meta":29,"style":29},"mutex\n",[31,7181,7182],{"__ignoreMap":29},[34,7183,7184],{"class":36,"line":37},[34,7185,7179],{"class":44},[12,7187,7188],{},"或者又获得其他 mutex，就可能产生非常复杂的锁依赖关系。",[12,7190,7191],{},"因此一个很实用的原则是：",[50,7193,7194],{},[12,7195,7196],{},"持锁期间尽量不要调用锁行为未知的外部代码。",[12,7198,7199],{},"特别是：",[24,7201,7204],{"className":7202,"code":7203,"language":404,"meta":29},[402],"回调函数\n虚函数\n第三方复杂函数\n用户传入函数\n",[31,7205,7203],{"__ignoreMap":29},[12,7207,7208],{},"都应该谨慎。",[146,7210],{},[470,7212,7214],{"id":7213},"三种常用-raii-锁怎么记","三种常用 RAII 锁怎么记",[12,7216,7217],{},"初学阶段可以记成：",[24,7219,7222],{"className":7220,"code":7221,"language":404,"meta":29},[402],"std::lock_guard\n│\n├─ 简单\n├─ 构造时加锁\n├─ 析构时解锁\n└─ 普通单锁场景优先考虑\n",[31,7223,7221],{"__ignoreMap":29},[24,7225,7228],{"className":7226,"code":7227,"language":404,"meta":29},[402],"std::unique_lock\n│\n├─ 更灵活\n├─ 可以 unlock()\n├─ 可以重新 lock()\n├─ 支持 defer_lock\n└─ 支持 try_to_lock\n",[31,7229,7227],{"__ignoreMap":29},[24,7231,7234],{"className":7232,"code":7233,"language":404,"meta":29},[402],"std::scoped_lock\n│\n├─ C++17\n├─ RAII\n├─ 可以管理一把锁\n└─ 特别适合同时管理多把锁\n",[31,7235,7233],{"__ignoreMap":29},[12,7237,7238],{},"最简单的记忆方式：",[24,7240,7243],{"className":7241,"code":7242,"language":404,"meta":29},[402],"普通单锁\n→ lock_guard\n\n灵活控制\n→ unique_lock\n\n同时多锁\n→ scoped_lock\n",[31,7244,7242],{"__ignoreMap":29},[146,7246],{},[149,7248,7249],{"id":7249},"常见错误",[470,7251,7252],{"id":7252},"只给写操作加锁",[24,7254,7255],{"className":26,"code":6184,"language":28,"meta":29,"style":29},[31,7256,7257,7265,7269,7279,7283,7293,7297,7301,7309,7313,7319],{"__ignoreMap":29},[34,7258,7259,7261,7263],{"class":36,"line":37},[34,7260,214],{"class":164},[34,7262,6193],{"class":40},[34,7264,220],{"class":44},[34,7266,7267],{"class":36,"line":172},[34,7268,226],{"class":44},[34,7270,7271,7273,7275,7277],{"class":36,"line":180},[34,7272,315],{"class":40},[34,7274,2738],{"class":44},[34,7276,918],{"class":40},[34,7278,2110],{"class":44},[34,7280,7281],{"class":36,"line":187},[34,7282,184],{"emptyLinePlaceholder":183},[34,7284,7285,7287,7289,7291],{"class":36,"line":206},[34,7286,6218],{"class":44},[34,7288,196],{"class":164},[34,7290,6223],{"class":199},[34,7292,203],{"class":44},[34,7294,7295],{"class":36,"line":211},[34,7296,289],{"class":44},[34,7298,7299],{"class":36,"line":223},[34,7300,184],{"emptyLinePlaceholder":183},[34,7302,7303,7305,7307],{"class":36,"line":229},[34,7304,190],{"class":164},[34,7306,6240],{"class":40},[34,7308,220],{"class":44},[34,7310,7311],{"class":36,"line":265},[34,7312,226],{"class":44},[34,7314,7315,7317],{"class":36,"line":271},[34,7316,1845],{"class":164},[34,7318,6253],{"class":44},[34,7320,7321],{"class":36,"line":280},[34,7322,289],{"class":44},[12,7324,7325],{},"如果存在并发写入，无同步读取同样可能产生数据竞争。",[146,7327],{},[470,7329,4428,7331,7333,7334],{"id":7330},"手动-lock-后忘记-unlock",[31,7332,83],{}," 后忘记 ",[31,7335,90],{},[12,7337,715],{},[24,7339,7341],{"className":26,"code":7340,"language":28,"meta":29,"style":29},"mutex.lock();\n\nif (error)\n{\n    return;\n}\n\nmutex.unlock();\n",[31,7342,7343,7351,7355,7362,7366,7372,7376,7380],{"__ignoreMap":29},[34,7344,7345,7347,7349],{"class":36,"line":37},[34,7346,915],{"class":44},[34,7348,918],{"class":40},[34,7350,353],{"class":44},[34,7352,7353],{"class":36,"line":172},[34,7354,184],{"emptyLinePlaceholder":183},[34,7356,7357,7359],{"class":36,"line":180},[34,7358,1270],{"class":164},[34,7360,7361],{"class":44}," (error)\n",[34,7363,7364],{"class":36,"line":187},[34,7365,226],{"class":44},[34,7367,7368,7370],{"class":36,"line":206},[34,7369,1845],{"class":164},[34,7371,203],{"class":44},[34,7373,7374],{"class":36,"line":211},[34,7375,289],{"class":44},[34,7377,7378],{"class":36,"line":223},[34,7379,184],{"emptyLinePlaceholder":183},[34,7381,7382,7384,7386],{"class":36,"line":229},[34,7383,915],{"class":44},[34,7385,933],{"class":40},[34,7387,353],{"class":44},[12,7389,7390],{},"因此普通场景优先使用 RAII 锁。",[146,7392],{},[470,7394,7395],{"id":7395},"多把锁的顺序不一致",[24,7397,7399],{"className":7398,"code":4663,"language":404,"meta":29},[402],[31,7400,4663],{"__ignoreMap":29},[12,7402,7403],{},"可能导致死锁。",[146,7405],{},[470,7407,7408],{"id":7408},"临界区太大",[12,7410,715],{},[24,7412,7414],{"className":26,"code":7413,"language":28,"meta":29,"style":29},"std::lock_guard lock(mutex);\n\nsleep();\nnetwork_io();\nfile_io();\nbig_calculation();\n",[31,7415,7416,7432,7436,7442,7449,7456],{"__ignoreMap":29},[34,7417,7418,7420,7422,7424,7426,7428,7430],{"class":36,"line":37},[34,7419,41],{"class":40},[34,7421,376],{"class":44},[34,7423,2257],{"class":40},[34,7425,2226],{"class":40},[34,7427,382],{"class":44},[34,7429,2266],{"class":40},[34,7431,2278],{"class":44},[34,7433,7434],{"class":36,"line":172},[34,7435,184],{"emptyLinePlaceholder":183},[34,7437,7438,7440],{"class":36,"line":180},[34,7439,2990],{"class":40},[34,7441,353],{"class":44},[34,7443,7444,7447],{"class":36,"line":187},[34,7445,7446],{"class":40},"network_io",[34,7448,353],{"class":44},[34,7450,7451,7454],{"class":36,"line":206},[34,7452,7453],{"class":40},"file_io",[34,7455,353],{"class":44},[34,7457,7458,7461],{"class":36,"line":211},[34,7459,7460],{"class":40},"big_calculation",[34,7462,353],{"class":44},[12,7464,7465],{},"其他线程可能长时间无法获得 mutex。",[146,7467],{},[470,7469,7470],{"id":7470},"临界区太小",[12,7472,7473],{},"本来应该整体更新的数据被拆开：",[24,7475,7478],{"className":7476,"code":7477,"language":404,"meta":29},[402],"锁\n修改 A\n解锁\n\n锁\n修改 B\n解锁\n",[31,7479,7477],{"__ignoreMap":29},[12,7481,7482],{},"可能让其他线程在中间看到不一致状态。",[146,7484],{},[470,7486,7487],{"id":7487},"持锁调用未知函数",[24,7489,7491],{"className":26,"code":7490,"language":28,"meta":29,"style":29},"std::lock_guard lock(mutex);\n\ncallback();\n",[31,7492,7493,7509,7513],{"__ignoreMap":29},[34,7494,7495,7497,7499,7501,7503,7505,7507],{"class":36,"line":37},[34,7496,41],{"class":40},[34,7498,376],{"class":44},[34,7500,2257],{"class":40},[34,7502,2226],{"class":40},[34,7504,382],{"class":44},[34,7506,2266],{"class":40},[34,7508,2278],{"class":44},[34,7510,7511],{"class":36,"line":172},[34,7512,184],{"emptyLinePlaceholder":183},[34,7514,7515,7518],{"class":36,"line":180},[34,7516,7517],{"class":40},"callback",[34,7519,353],{"class":44},[12,7521,7522],{},"如果 callback 内部又涉及锁，可能造成死锁或复杂锁依赖。",[146,7524],{},[470,7526,7528],{"id":7527},"为了安全到处加锁","为了“安全”到处加锁",[12,7530,7531],{},"锁不是越多越安全。",[12,7533,7534],{},"锁越多，可能带来：",[24,7536,7539],{"className":7537,"code":7538,"language":404,"meta":29},[402],"更复杂的锁关系\n↓\n更多线程竞争\n↓\n更差的性能\n↓\n更高的死锁风险\n",[31,7540,7538],{"__ignoreMap":29},[12,7542,7543],{},"真正应该做的是：",[50,7545,7546],{},[12,7547,7548],{},"明确哪一份共享状态由哪一把 mutex 保护。",[146,7550],{},[470,7552,7554],{"id":7553},"认为-mutex-会自动保护变量","认为 mutex 会自动保护变量",[12,7556,7557],{},"下面：",[24,7559,7561],{"className":26,"code":7560,"language":28,"meta":29,"style":29},"std::mutex mutex;\nint counter;\n",[31,7562,7563,7569],{"__ignoreMap":29},[34,7564,7565,7567],{"class":36,"line":37},[34,7566,41],{"class":40},[34,7568,881],{"class":44},[34,7570,7571,7573],{"class":36,"line":172},[34,7572,190],{"class":164},[34,7574,7575],{"class":44}," counter;\n",[12,7577,7578],{},"并不会自动建立：",[24,7580,7583],{"className":7581,"code":7582,"language":404,"meta":29},[402],"mutex 保护 counter\n",[31,7584,7582],{"__ignoreMap":29},[12,7586,7587],{},"的关系。",[12,7589,7590],{},"必须让所有线程遵守：",[24,7592,7595],{"className":7593,"code":7594,"language":404,"meta":29},[402],"访问 counter 前\n必须按照约定获得 mutex\n",[31,7596,7594],{"__ignoreMap":29},[12,7598,7599],{},"mutex 才真正起作用。",[146,7601],{},[470,7603,7605,7606,7608],{"id":7604},"认为-shared_mutex-一定更快","认为 ",[31,7607,5930],{}," 一定更快",[12,7610,7611,7613],{},[31,7612,5930],{}," 允许多个线程同时读，但管理成本也更高。",[12,7615,7616],{},"只有真正：",[24,7618,7621],{"className":7619,"code":7620,"language":404,"meta":29},[402],"读很多\n写很少\n存在明显并发读取需求\n",[31,7622,7620],{"__ignoreMap":29},[12,7624,7625],{},"时才值得考虑。",[146,7627],{},[149,7629,7631],{"id":7630},"最重要的-mutex-心智模型","最重要的 mutex 心智模型",[12,7633,6309],{},[24,7635,7637],{"className":26,"code":7636,"language":28,"meta":29,"style":29},"std::mutex mutex;\n",[31,7638,7639],{"__ignoreMap":29},[34,7640,7641,7643],{"class":36,"line":37},[34,7642,41],{"class":40},[34,7644,881],{"class":44},[12,7646,7647],{},"不要简单理解成：",[50,7649,7650],{},[12,7651,7652],{},"创建了一把可以锁变量的锁。",[12,7654,7655],{},"更好的理解是：",[50,7657,7658],{},[12,7659,7660],{},"创建了一个多个线程竞争“进入某段临界区资格”的同步对象。",[12,7662,6309],{},[24,7664,7665],{"className":26,"code":5058,"language":28,"meta":29,"style":29},[31,7666,7667],{"__ignoreMap":29},[34,7668,7669,7671,7673,7675,7677,7679,7681],{"class":36,"line":37},[34,7670,41],{"class":40},[34,7672,376],{"class":44},[34,7674,2257],{"class":40},[34,7676,2226],{"class":40},[34,7678,382],{"class":44},[34,7680,2266],{"class":40},[34,7682,2278],{"class":44},[12,7684,5576],{},[24,7686,7689],{"className":7687,"code":7688,"language":404,"meta":29},[402],"线程想进入临界区\n        ↓\n先获得 mutex\n        ↓\n获得成功\n        ↓\n进入临界区\n        ↓\n离开作用域\n        ↓\n自动释放 mutex\n",[31,7690,7688],{"__ignoreMap":29},[12,7692,7693],{},"其他线程：",[24,7695,7698],{"className":7696,"code":7697,"language":404,"meta":29},[402],"暂时拿不到 mutex\n↓\n不能同时进入\n",[31,7699,7697],{"__ignoreMap":29},[12,7701,7702],{},"这才是 mutex 最核心的思想。",[146,7704],{},[149,7706,7707],{"id":7707},"小结",[12,7709,7710,7712],{},[31,7711,77],{}," 最核心的作用是：",[50,7714,7715],{},[12,7716,7717],{},"让多个线程互斥访问共享可变状态。",[12,7719,7720],{},"最基础的三个操作：",[24,7722,7724],{"className":26,"code":7723,"language":28,"meta":29,"style":29},"lock()\ntry_lock()\nunlock()\n",[31,7725,7726,7732,7738],{"__ignoreMap":29},[34,7727,7728,7730],{"class":36,"line":37},[34,7729,918],{"class":40},[34,7731,220],{"class":44},[34,7733,7734,7736],{"class":36,"line":172},[34,7735,1037],{"class":40},[34,7737,220],{"class":44},[34,7739,7740,7742],{"class":36,"line":180},[34,7741,933],{"class":40},[34,7743,220],{"class":44},[12,7745,7746],{},"但实际开发中，普通场景不推荐手动写：",[24,7748,7750],{"className":26,"code":7749,"language":28,"meta":29,"style":29},"mutex.lock();\n\n...\n\nmutex.unlock();\n",[31,7751,7752,7760,7764,7768,7772],{"__ignoreMap":29},[34,7753,7754,7756,7758],{"class":36,"line":37},[34,7755,915],{"class":44},[34,7757,918],{"class":40},[34,7759,353],{"class":44},[34,7761,7762],{"class":36,"line":172},[34,7763,184],{"emptyLinePlaceholder":183},[34,7765,7766],{"class":36,"line":180},[34,7767,1747],{"class":44},[34,7769,7770],{"class":36,"line":187},[34,7771,184],{"emptyLinePlaceholder":183},[34,7773,7774,7776,7778],{"class":36,"line":206},[34,7775,915],{"class":44},[34,7777,933],{"class":40},[34,7779,353],{"class":44},[12,7781,7782],{},"而应该优先让 RAII 对象管理锁。",[12,7784,7785],{},"普通单锁场景：",[24,7787,7788],{"className":26,"code":2167,"language":28,"meta":29,"style":29},[31,7789,7790],{"__ignoreMap":29},[34,7791,7792,7794],{"class":36,"line":37},[34,7793,41],{"class":40},[34,7795,2176],{"class":44},[12,7797,7798],{},"需要更加灵活地控制锁生命周期：",[24,7800,7801],{"className":26,"code":3638,"language":28,"meta":29,"style":29},[31,7802,7803],{"__ignoreMap":29},[34,7804,7805,7807],{"class":36,"line":37},[34,7806,41],{"class":40},[34,7808,3647],{"class":44},[12,7810,7811],{},"需要同时管理多把锁：",[24,7813,7814],{"className":26,"code":4845,"language":28,"meta":29,"style":29},[31,7815,7816],{"__ignoreMap":29},[34,7817,7818,7820],{"class":36,"line":37},[34,7819,41],{"class":40},[34,7821,4854],{"class":44},[12,7823,7824],{},"需要多读单写：",[24,7826,7827],{"className":26,"code":5897,"language":28,"meta":29,"style":29},[31,7828,7829],{"__ignoreMap":29},[34,7830,7831,7833],{"class":36,"line":37},[34,7832,41],{"class":40},[34,7834,5906],{"class":44},[12,7836,7837],{},"需要带超时地等待：",[24,7839,7840],{"className":26,"code":5597,"language":28,"meta":29,"style":29},[31,7841,7842],{"__ignoreMap":29},[34,7843,7844,7846],{"class":36,"line":37},[34,7845,41],{"class":40},[34,7847,5606],{"class":44},[12,7849,7850],{},"确实需要同一个线程重复进入同一个临界区时：",[24,7852,7853],{"className":26,"code":5500,"language":28,"meta":29,"style":29},[31,7854,7855],{"__ignoreMap":29},[34,7856,7857,7859],{"class":36,"line":37},[34,7858,41],{"class":40},[34,7860,5509],{"class":44},[12,7862,7863],{},"需要让某段初始化逻辑只成功执行一次：",[24,7865,7866],{"className":26,"code":6778,"language":28,"meta":29,"style":29},[31,7867,7868],{"__ignoreMap":29},[34,7869,7870,7872],{"class":36,"line":37},[34,7871,41],{"class":40},[34,7873,6449],{"class":44},[12,7875,7876],{},"并发代码中真正需要思考的，通常不是：",[24,7878,7881],{"className":7879,"code":7880,"language":404,"meta":29},[402],"mutex API 怎么写？\n",[31,7882,7880],{"__ignoreMap":29},[12,7884,7885],{},"而是：",[24,7887,7890],{"className":7888,"code":7889,"language":404,"meta":29},[402],"哪些数据会被多个线程共享？\n哪些操作必须作为一个整体？\n哪把 mutex 负责保护这些状态？\n临界区应该有多大？\n有没有同时获取多把锁？\n不同地方的锁顺序是否一致？\n",[31,7891,7889],{"__ignoreMap":29},[12,7893,7894],{},"把这些问题想明白以后，mutex 本身的代码通常反而并不复杂。",[12,7896,7897,7898,129,7901,7904],{},"对于“单个简单共享变量”的同步，以及“让线程等待某个条件发生”这两类问题，后面的 ",[31,7899,7900],{},"std::atomic",[31,7902,7903],{},"std::condition_variable"," 章节会分别继续介绍。",[7906,7907,7908],"style",{},"html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":29,"searchDepth":172,"depth":172,"links":7910},[7911,7917,7929,7941,7955,7962,7968,7973,7980,7993,7994],{"id":151,"depth":172,"text":151,"children":7912},[7913,7915,7916],{"id":472,"depth":180,"text":7914},"++counter 并不是不可分割的一步",{"id":575,"depth":180,"text":575},{"id":662,"depth":180,"text":662},{"id":760,"depth":172,"text":7918,"children":7919},"std::mutex 基础",[7920,7922,7923,7925,7927],{"id":766,"depth":180,"text":7921},"std::mutex 到底是什么",{"id":851,"depth":180,"text":852},{"id":994,"depth":180,"text":7924},"std::mutex 的基本使用",{"id":1445,"depth":180,"text":7926},"用 mutex 修复 counter",{"id":1761,"depth":180,"text":7928},"为什么不推荐手写 lock() \u002F unlock()",{"id":2063,"depth":172,"text":2064,"children":7930},[7931,7932,7933,7935,7937,7938,7939,7940],{"id":2067,"depth":180,"text":2068},{"id":2159,"depth":180,"text":107},{"id":2376,"depth":180,"text":7934},"用 lock_guard 改写 counter",{"id":2677,"depth":180,"text":7936},"可以用 {} 主动缩小锁的作用域",{"id":2803,"depth":180,"text":2803},{"id":3010,"depth":180,"text":3010},{"id":3200,"depth":180,"text":3201},{"id":3372,"depth":180,"text":3373},{"id":3607,"depth":172,"text":7942,"children":7943},"std::unique_lock （常用）",[7944,7946,7948,7950,7952,7953],{"id":3705,"depth":180,"text":7945},"unique_lock 可以提前解锁",{"id":3785,"depth":180,"text":7947},"unique_lock 可以再次加锁",{"id":3889,"depth":180,"text":7949},"延迟加锁：std::defer_lock",{"id":4047,"depth":180,"text":7951},"尝试加锁：std::try_to_lock",{"id":4105,"depth":180,"text":4144},{"id":4291,"depth":180,"text":7954},"lock_guard 和 unique_lock 怎么选",{"id":4519,"depth":172,"text":4519,"children":7956},[7957,7958,7959,7960,7961],{"id":4522,"depth":180,"text":4522},{"id":4696,"depth":180,"text":4696},{"id":4741,"depth":180,"text":4742},{"id":4829,"depth":180,"text":119},{"id":5093,"depth":180,"text":5096},{"id":5376,"depth":172,"text":5377,"children":7963},[7964,7965,7966],{"id":5380,"depth":180,"text":128},{"id":5557,"depth":180,"text":132},{"id":5815,"depth":180,"text":7967},"std::shared_mutex：多读单写",{"id":6379,"depth":172,"text":6379,"children":7969},[7970,7971],{"id":6382,"depth":180,"text":143},{"id":6695,"depth":180,"text":7972},"函数局部 static 初始化也是线程安全的",{"id":6791,"depth":172,"text":6792,"children":7974},[7975,7976,7978,7979],{"id":6795,"depth":180,"text":6796},{"id":7002,"depth":180,"text":7977},"为什么 mutex 经常写成 mutable （重要）",{"id":7126,"depth":180,"text":7126},{"id":7213,"depth":180,"text":7214},{"id":7249,"depth":172,"text":7249,"children":7981},[7982,7983,7985,7986,7987,7988,7989,7990,7991],{"id":7252,"depth":180,"text":7252},{"id":7330,"depth":180,"text":7984},"手动 lock() 后忘记 unlock()",{"id":7395,"depth":180,"text":7395},{"id":7408,"depth":180,"text":7408},{"id":7470,"depth":180,"text":7470},{"id":7487,"depth":180,"text":7487},{"id":7527,"depth":180,"text":7528},{"id":7553,"depth":180,"text":7554},{"id":7604,"depth":180,"text":7992},"认为 shared_mutex 一定更快",{"id":7630,"depth":172,"text":7631},{"id":7707,"depth":172,"text":7707},"2000-1900-1200","2023-10-05","多个线程同时运行时，如果它们会访问同一份共享数据，并且其中至少有一个线程会修改这份数据，就必须开始考虑线程同步问题。","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-1200-mutex-lock-guard",{"title":5,"description":7997},"wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1200-mutex-lock-guard","LMHVwGdPZv_j--HQ-g9rlFD0N_jSou7I2n2MutYuEvU",[8009,8016,8022,8028,8034,8040,8046,8052,8058,8064,8070,8076,8082,8088,8094,8100,8106,8112,8118,8124,8130,8136,8142,8148,8154,8160,8166,8172,8178,8184,8190,8196,8202,8208,8214,8220,8226,8232,8238,8244,8250,8256,8262,8268,8274,8280,8286,8292,8298,8304,8310,8316,8322,8328,8334,8340,8346,8352,8358,8364,8370,8376,8382,8384,8389,8394,8400,8406,8412,8418,8424,8430,8436,8442,8448,8454,8460,8466,8472,8478,8484,8490,8496,8502,8508,8514,8520,8526,8532,8538,8544,8550],{"path":8010,"stem":8011,"title":8012,"authors":8013,"date":7996,"chapterOrder":8014,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8017,"stem":8018,"title":8019,"authors":8020,"date":7996,"chapterOrder":8021,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0200-c-ji-chu-chu-shi","wiki\u002F2023-10-05-Cplusplus教学\u002F0200-C++基础初识","C++基础初识",[7],"0200",{"path":8023,"stem":8024,"title":8025,"authors":8026,"date":7996,"chapterOrder":8027,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8029,"stem":8030,"title":8031,"authors":8032,"date":7996,"chapterOrder":8033,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0400-shu-ru-shu-chu","wiki\u002F2023-10-05-Cplusplus教学\u002F0400-输入输出","输入输出",[7],"0400",{"path":8035,"stem":8036,"title":8037,"authors":8038,"date":7996,"chapterOrder":8039,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0500-yun-suan-fu","wiki\u002F2023-10-05-Cplusplus教学\u002F0500-运算符","运算符",[7],"0500",{"path":8041,"stem":8042,"title":8043,"authors":8044,"date":7996,"chapterOrder":8045,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0600-cheng-xu-liu-cheng-jie-gou","wiki\u002F2023-10-05-Cplusplus教学\u002F0600-程序流程结构","程序流程结构",[7],"0600",{"path":8047,"stem":8048,"title":8049,"authors":8050,"date":7996,"chapterOrder":8051,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0700-shu-zu","wiki\u002F2023-10-05-Cplusplus教学\u002F0700-数组","数组",[7],"0700",{"path":8053,"stem":8054,"title":8055,"authors":8056,"date":7996,"chapterOrder":8057,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0800-han-shu-yu-tou-wen-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F0800-函数与头文件","函数与头文件",[7],"0800",{"path":8059,"stem":8060,"title":8061,"authors":8062,"date":7996,"chapterOrder":8063,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F0900-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F0900-指针","指针",[7],"0900",{"path":8065,"stem":8066,"title":8067,"authors":8068,"date":7996,"chapterOrder":8069,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1000-jie-gou-ti-yu-gong-yong-ti","wiki\u002F2023-10-05-Cplusplus教学\u002F1000-结构体与共用体","结构体与共用体",[7],"1000",{"path":8071,"stem":8072,"title":8073,"authors":8074,"date":7996,"chapterOrder":8075,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8077,"stem":8078,"title":8079,"authors":8080,"date":7996,"chapterOrder":8081,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1200-yin-yong","wiki\u002F2023-10-05-Cplusplus教学\u002F1200-引用","引用",[7],"1200",{"path":8083,"stem":8084,"title":8085,"authors":8086,"date":7996,"chapterOrder":8087,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1300-han-shu-ti-gao","wiki\u002F2023-10-05-Cplusplus教学\u002F1300-函数提高","函数提高",[7],"1300",{"path":8089,"stem":8090,"title":8091,"authors":8092,"date":7996,"chapterOrder":8093,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1350-ming-ming-kong-jian","wiki\u002F2023-10-05-Cplusplus教学\u002F1350-命名空间","命名空间",[7],"1350",{"path":8095,"stem":8096,"title":8097,"authors":8098,"date":7996,"chapterOrder":8099,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0100-feng-zhuang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0100-封装","封装",[7],"1400-0100",{"path":8101,"stem":8102,"title":8103,"authors":8104,"date":7996,"chapterOrder":8105,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8107,"stem":8108,"title":8109,"authors":8110,"date":7996,"chapterOrder":8111,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8113,"stem":8114,"title":8115,"authors":8116,"date":7996,"chapterOrder":8117,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8119,"stem":8120,"title":8121,"authors":8122,"date":7996,"chapterOrder":8123,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0500-ji-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0500-继承","继承",[7],"1400-0500",{"path":8125,"stem":8126,"title":8127,"authors":8128,"date":7996,"chapterOrder":8129,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-0600-duo-tai","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-0600-多态","多态",[7],"1400-0600",{"path":8131,"stem":8132,"title":8133,"authors":8134,"date":7996,"chapterOrder":8135,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1400-lei-he-dui-xiang","wiki\u002F2023-10-05-Cplusplus教学\u002F1400-类和对象","类和对象",[7],"1400",{"path":8137,"stem":8138,"title":8139,"authors":8140,"date":7996,"chapterOrder":8141,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1450-han-shu-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F1450-函数指针","函数指针",[7],"1450",{"path":8143,"stem":8144,"title":8145,"authors":8146,"date":7996,"chapterOrder":8147,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1500-wen-jian-cao-zuo","wiki\u002F2023-10-05-Cplusplus教学\u002F1500-文件操作","文件操作",[7],"1500",{"path":8149,"stem":8150,"title":8151,"authors":8152,"date":7996,"chapterOrder":8153,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8155,"stem":8156,"title":8157,"authors":8158,"date":7996,"chapterOrder":8159,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1700-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F1700-模板","模板",[7],"1700",{"path":8161,"stem":8162,"title":8163,"authors":8164,"date":7996,"chapterOrder":8165,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8167,"stem":8168,"title":8169,"authors":8170,"date":7996,"chapterOrder":8171,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8173,"stem":8174,"title":8175,"authors":8176,"date":7996,"chapterOrder":8177,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8179,"stem":8180,"title":8181,"authors":8182,"date":7996,"chapterOrder":8183,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8185,"stem":8186,"title":8187,"authors":8188,"date":7996,"chapterOrder":8189,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8191,"stem":8192,"title":8193,"authors":8194,"date":7996,"chapterOrder":8195,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8197,"stem":8198,"title":8199,"authors":8200,"date":7996,"chapterOrder":8201,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8203,"stem":8204,"title":8205,"authors":8206,"date":7996,"chapterOrder":8207,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8209,"stem":8210,"title":8211,"authors":8212,"date":7996,"chapterOrder":8213,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8215,"stem":8216,"title":8217,"authors":8218,"date":7996,"chapterOrder":8219,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8221,"stem":8222,"title":8223,"authors":8224,"date":7996,"chapterOrder":8225,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8227,"stem":8228,"title":8229,"authors":8230,"date":7996,"chapterOrder":8231,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-1200-chang-yong-suan-fa","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-1200-常用算法","常用算法",[7],"1800-1200",{"path":8233,"stem":8234,"title":8235,"authors":8236,"date":7996,"chapterOrder":8237,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1800-stl-ti-gao-bian-cheng","wiki\u002F2023-10-05-Cplusplus教学\u002F1800-STL提高编程","STL提高编程",[7],"1800",{"path":8239,"stem":8240,"title":8241,"authors":8242,"date":7996,"chapterOrder":8243,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F1900-c-lei-xing-zhuan-huan","wiki\u002F2023-10-05-Cplusplus教学\u002F1900-C++类型转换","C++类型转换",[7],"1900",{"path":8245,"stem":8246,"title":8247,"authors":8248,"date":7996,"chapterOrder":8249,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0100-auto","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0100-auto","auto",[7],"2000-0100",{"path":8251,"stem":8252,"title":8253,"authors":8254,"date":7996,"chapterOrder":8255,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0200-nullptr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0200-nullptr","nullptr",[7],"2000-0200",{"path":8257,"stem":8258,"title":8259,"authors":8260,"date":7996,"chapterOrder":8261,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0300-using","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0300-using","using",[7],"2000-0300",{"path":8263,"stem":8264,"title":8265,"authors":8266,"date":7996,"chapterOrder":8267,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8269,"stem":8270,"title":8271,"authors":8272,"date":7996,"chapterOrder":8273,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8275,"stem":8276,"title":8277,"authors":8278,"date":7996,"chapterOrder":8279,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8281,"stem":8282,"title":8283,"authors":8284,"date":7996,"chapterOrder":8285,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0700-constexpr","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0700-constexpr","constexpr",[7],"2000-0700",{"path":8287,"stem":8288,"title":8289,"authors":8290,"date":7996,"chapterOrder":8291,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0800-raii","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0800-RAII","RAII",[7],"2000-0800",{"path":8293,"stem":8294,"title":8295,"authors":8296,"date":7996,"chapterOrder":8297,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-0900-zhi-neng-zhi-zhen","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-0900-智能指针","智能指针",[7],"2000-0900",{"path":8299,"stem":8300,"title":8301,"authors":8302,"date":7996,"chapterOrder":8303,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8305,"stem":8306,"title":8307,"authors":8308,"date":7996,"chapterOrder":8309,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8311,"stem":8312,"title":8313,"authors":8314,"date":7996,"chapterOrder":8315,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8317,"stem":8318,"title":8319,"authors":8320,"date":7996,"chapterOrder":8321,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8323,"stem":8324,"title":8325,"authors":8326,"date":7996,"chapterOrder":8327,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8329,"stem":8330,"title":8331,"authors":8332,"date":7996,"chapterOrder":8333,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8335,"stem":8336,"title":8337,"authors":8338,"date":7996,"chapterOrder":8339,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8341,"stem":8342,"title":8343,"authors":8344,"date":7996,"chapterOrder":8345,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8347,"stem":8348,"title":8349,"authors":8350,"date":7996,"chapterOrder":8351,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8353,"stem":8354,"title":8355,"authors":8356,"date":7996,"chapterOrder":8357,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8359,"stem":8360,"title":8361,"authors":8362,"date":7996,"chapterOrder":8363,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8365,"stem":8366,"title":8367,"authors":8368,"date":7996,"chapterOrder":8369,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8371,"stem":8372,"title":8373,"authors":8374,"date":7996,"chapterOrder":8375,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8377,"stem":8378,"title":8379,"authors":8380,"date":7996,"chapterOrder":8381,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8004,"stem":8006,"title":5,"authors":8383,"date":7996,"chapterOrder":7995,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},[7],{"path":8385,"stem":8386,"title":7900,"authors":8387,"date":7996,"chapterOrder":8388,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-1900-1300-std-atomic","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-1900-1300-std-atomic",[7],"2000-1900-1300",{"path":8390,"stem":8391,"title":4472,"authors":8392,"date":7996,"chapterOrder":8393,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8395,"stem":8396,"title":8397,"authors":8398,"date":7996,"chapterOrder":8399,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8401,"stem":8402,"title":8403,"authors":8404,"date":7996,"chapterOrder":8405,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8407,"stem":8408,"title":8409,"authors":8410,"date":7996,"chapterOrder":8411,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8413,"stem":8414,"title":8415,"authors":8416,"date":7996,"chapterOrder":8417,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8419,"stem":8420,"title":8421,"authors":8422,"date":7996,"chapterOrder":8423,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8425,"stem":8426,"title":8427,"authors":8428,"date":7996,"chapterOrder":8429,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8431,"stem":8432,"title":8433,"authors":8434,"date":7996,"chapterOrder":8435,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2000-xian-dai-c","wiki\u002F2023-10-05-Cplusplus教学\u002F2000-现代C++","现代C++",[7],"2000",{"path":8437,"stem":8438,"title":8439,"authors":8440,"date":7996,"chapterOrder":8441,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8443,"stem":8444,"title":8445,"authors":8446,"date":7996,"chapterOrder":8447,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8449,"stem":8450,"title":8451,"authors":8452,"date":7996,"chapterOrder":8453,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8455,"stem":8456,"title":8457,"authors":8458,"date":7996,"chapterOrder":8459,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8461,"stem":8462,"title":8463,"authors":8464,"date":7996,"chapterOrder":8465,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8467,"stem":8468,"title":8469,"authors":8470,"date":7996,"chapterOrder":8471,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8473,"stem":8474,"title":8475,"authors":8476,"date":7996,"chapterOrder":8477,"chapterDepth":172,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8479,"stem":8480,"title":8481,"authors":8482,"date":7996,"chapterOrder":8483,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8485,"stem":8486,"title":8487,"authors":8488,"date":7996,"chapterOrder":8489,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8491,"stem":8492,"title":8493,"authors":8494,"date":7996,"chapterOrder":8495,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8497,"stem":8498,"title":8499,"authors":8500,"date":7996,"chapterOrder":8501,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8503,"stem":8504,"title":8505,"authors":8506,"date":7996,"chapterOrder":8507,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8509,"stem":8510,"title":8511,"authors":8512,"date":7996,"chapterOrder":8513,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2100-chang-yong-ku","wiki\u002F2023-10-05-Cplusplus教学\u002F2100-常用库","常用库学习",[7],"2100",{"path":8515,"stem":8516,"title":8517,"authors":8518,"date":7996,"chapterOrder":8519,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8521,"stem":8522,"title":8523,"authors":8524,"date":7996,"chapterOrder":8525,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8527,"stem":8528,"title":8529,"authors":8530,"date":7996,"chapterOrder":8531,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8533,"stem":8534,"title":8535,"authors":8536,"date":7996,"chapterOrder":8537,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8539,"stem":8540,"title":8541,"authors":8542,"date":7996,"chapterOrder":8543,"chapterDepth":37,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\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":8545,"stem":8546,"title":8547,"authors":8548,"date":7996,"chapterOrder":8549,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":8002},"\u002Fwiki\u002F2023-10-05-cplusplus-jiao-xue\u002F2200-cmake-gong-cheng-mu-ban","wiki\u002F2023-10-05-Cplusplus教学\u002F2200-CMake工程模板","CMake工程模板",[7],"2200",{"path":7999,"stem":8551,"title":8552,"authors":8553,"date":7996,"chapterOrder":8554,"chapterDepth":8015,"docKey":7998,"docRoot":7999,"docTitle":8000,"isWikiDoc":183,"isWikiIndex":183},"wiki\u002F2023-10-05-Cplusplus教学\u002Findex","C\u002FC++教程",[7],null,[8556,8558,8561,8564],{"name":7,"image":8557},"\u002Fimages\u002Fmember_photo\u002F董佳辉.jpg",{"name":8559,"image":8560},"宫金良","\u002Fimages\u002Fmember_photo\u002F宫金良.jpg",{"name":8562,"image":8563},"巩丽","\u002Fimages\u002Fmember_photo\u002F巩丽.png",{"name":8565,"image":8566},"张彦斐","\u002Fimages\u002Fmember_photo\u002F张彦斐.jpg",1792431898968]