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