当前位置: 首页 > news >正文

模板网站修改教程做任务挣钱的网站

模板网站修改教程,做任务挣钱的网站,php wordpress,做网站怎么宣传运营目录 一、C11的简介 二、万能引用与完美转发 1、万能引用#xff1a;模板中的 引用 2、完美转发#xff1a;保持万能引用左右值属性的解决方案 三、可变参数模板 1、可变参数模板的基本使用 2、push 系列和 emplace 系列的区别 四、lambda表达式#xf…目录 一、C11的简介 二、万能引用与完美转发 1、万能引用模板中的 引用 2、完美转发保持万能引用左右值属性的解决方案 三、可变参数模板  1、可变参数模板的基本使用 2、push 系列和 emplace 系列的区别 四、lambda表达式重点 1、函数对象又名仿函数 2、lambda 表达式 2.1  lambda 表达式的书写格式 1、捕捉列表 2、参数列表 3、mutable 4、-返回值类型 5、函数体 6、小总结 3、各种类型的 lambda 表达式代码示例 4、lambda表达式的一些注意事项  5、lambda表达式的底层原理 五、常用包装器function  1、代码引入 2、function 包装器的作用 3、function 包装器的使用 3.1  模板原型 3.2  使用举例 六、通用函数适配器bind  1、模板原型 2、使用举例重点 七、C11标准线程库 0、C标准线程库常用函数表格 1、标准线程库使用的一些注意事项 2、线程函数的参数 3、线程函数的返回值 4、原子性操作库atomic 4.1  C98 线程安全解决方案对欲修改的共享数据加锁 4.1.1  互斥锁的类型 4.1.2  加锁解锁的函数 4.1.3  死锁问题 4.1.4  lock_guard VS unique_lock小重点 4.2  C11 提供的另一种线程安全解决方案原子操作 本章将介绍继C98以来更新最大的一个标准也是实际开发中用的最多的重要标准C11标准。 由于C11增加了非常多的语法特性笔者学识有限也很难一一介绍在此主要讲解一些比较实用的语法。 一、C11的简介 公元2003年C标准委员会曾经提交了一份技术勘误表(简称TC1)使得 C03 这个名字取代了 C98 称为 C11 之前的最新C标准名称。 不过由于 C03(TC1) 主要是对 C98 标准中的漏洞进行修复并没有改动语言的核心部分因此人们习惯性的把两个标准合并称为 C98/03标准。 从 C0x 到 C11C标准十年磨一剑第二个真正意义上的标准C11才终于珊珊来迟。 相较于 C98/03C11带来了数量可观的变化——其中包含了约140个新特性以及对C03标准中约600个缺陷的修正这使得C11更像是从C98/03中孕育出的一种新语言。 相比较而言 C11能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全不仅功能更强大而且能提升程序员的开发效率公司实际项目开发中也用得比较多值得重点去学习。 我们在前文中已经提到了两个C11标准中的重要内容 《C/C四类和对象》第五小节第3小点右值引用和移动语义 《C/C七RAII思想与智能指针》全文 因此关于这两篇文章所提到的有关C11的知识点本章在此不做赘述诸位看官若感兴趣可直接点击跳转至相应文章观看。 cppreferenceC学习手册中有关C11标准的详细介绍 二、万能引用与完美转发 1、万能引用模板中的 引用 我们在 C/C四中介绍了右值引用即 引用作用是给右值取别名 但是在模板中 就不是代表右值引用了编译器会把其处理为万能引用——即既能接收左值引用又能接收右值引用。又名引用折叠即传右值为右值引用传左值 会被折叠成 即左值引用 我们给出一段代码 #include iostream using namespace std;void Fun(int x) { cout 左值引用 endl; } void Fun(const int x) { cout const 左值引用 endl; }void Fun(int x) { cout 右值引用 endl; } void Fun(const int x) { cout const 右值引用 endl; }templatetypename T void PerfectForward(T t) {Fun(t); }int main() {PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b 8; PerfectForward(b); // const 左值 PerfectForward(std::move(b)); // const 右值return 0; } 观察运行结果发现全部变成了左值引用 运行结果为什么会是这样呢  这是因为模板的万能引用只是提供了可以同时接受左值引用和右值引用的能力但是当把 t 直接传递给 Fun 函数时t 已经是一个具有名字的变量因此它总是被视为一个左值。 如果我们想在传递参数过程中始终保持其左右值属性怎么办呢这时候就需要完美转发了。 2、完美转发保持万能引用左右值属性的解决方案 完美转发的关键字是 std::forward模板类型名( 参数名 )作用是可以保持传参时参数的属性传左值就是左值传右值就是右值。 我们修改一下上面的代码 #include iostream using namespace std;void Fun(int x) { cout 左值引用 endl; } void Fun(const int x) { cout const 左值引用 endl; }void Fun(int x) { cout 右值引用 endl; } void Fun(const int x) { cout const 右值引用 endl; }templatetypename T void PerfectForward(T t) {Fun(forwardT(t)); }int main() {PerfectForward(10); // 右值int a;PerfectForward(a); // 左值PerfectForward(std::move(a)); // 右值const int b 8; PerfectForward(b); // const 左值 PerfectForward(std::move(b)); // const 右值return 0; } 可以发现运行结果正确了 三、可变参数模板  1、可变参数模板的基本使用 在C98/03中类模板和函数模板中只能包含固定数量的模板参数 而C11引入了新特性可变参数模板让我们可以接受可变参数的函数模板与类模板。 由于可变参数模板使用比较抽象晦涩在此点到为止只介绍一些基础的可变参数模板特性便足够使用了。 下面这个就是一个基本可变参数的函数模板 // Args是一个模板可变参数包表示模板可以接受任意数量和类型的参数 template class ...Args// args是一个函数可变参数包表示函数可以接受任意数量和类型的参数。 void Test(Args... args) {} 可以看到模板可变参数是在模板类型名前面加上 ... 函数可变参数就是在类型名后面加上 ... 。 我们把带省略号的参数称作“参数包”里面可以包含任意个参数。 2、push 系列和 emplace 系列的区别 C使用手册中vector容器的emplace_back函数 C使用手册中 list 容器的emplace_back函数 template class... Args void emplace_back (Args... args); 我们可以看到emplace 系列的接口相较于push 系列都是支持万能引用与可变参数模板的。 #include iostream #include list using namespace std;int main() {// 下面我们试一下带有拷贝构造和移动构造的 string 类型// 我们会发现差别emplace_back是直接构造了push_back是先构造再移动构造std::list std::pairint, string mylist;// 多参数时可以分开一个个传参因为 emplace_back 的形参是可变参数包直接把参数包不断往下传直接构造到节点上mylist.emplace_back(10, sort);mylist.emplace_back(make_pair(20, sort));// 隐式类型转换先接受参数构造再移动构造mylist.push_back(make_pair(30, sort));mylist.push_back({ 40, sort });for (auto e : mylist){cout e.first : e.second endl;}return 0; } 所以 push 系列接口适用于已经存在的对象需要先构造对象再复制或移动到容器中。emplace 系列接口适用于直接在容器中构造对象避免了不必要的临时对象创建和销毁提高了性能。浅拷贝时以及参数较多时相较于 push 系列性能提升巨大深拷贝时也会高效一些但提升幅度没有这么大 四、lambda表达式重点 1、函数对象又名仿函数 函数对象又名仿函数即可以像函数一样使用的对象实际上就是在类里面重载了 operator() 运算符的类对象。 在C98中如果想要为某些自定义元素排序就需要利用仿函数来定义排序时的比较规则 #include iostream #include algorithm #include vector using namespace std;struct Goods {Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}string _name; // 名字double _price; // 价格int _evaluate; // 评价 }; struct ComparePriceLess {// 价格倒序排序bool operator()(const Goods gl, const Goods gr){return gl._price gr._price;} }; struct ComparePriceGreater {bool operator()(const Goods gl, const Goods gr){// 价格正序排序return gl._price gr._price;} };int main() {vectorGoods v { { 苹果, 2.1, 5 }, { 香蕉, 3, 4 }, { 橙子, 2.2, 3 }, { 菠萝, 1.5, 4 } };sort(v.begin(), v.end(), ComparePriceLess());sort(v.begin(), v.end(), ComparePriceGreater());} 我们可以发现为了实现自定义类型元素的排序针对每一种排序规则都要去实现一个类类里还要重载operator()显得有些不便。 因此C11中出现了lambda表达式用来轻量级代替函数对象 2、lambda 表达式 2.1  lambda 表达式的书写格式 [ 捕捉列表 ](参数列表)mutable -返回值类型{函数体} 1、捕捉列表 捕捉列表总是出现在 lambda 表达式的开头编译器根据捕捉列表来判断接下来的代码是否为 lambda 函数。因此必须要写不能省略 作用可以根据列表捕捉项来捕捉 lambda 表达式所在的函数的作用域中的可用变量使其直接成为 lambda 函数的成员变量。 列表捕捉项描述了上下文中哪些数据可以被lambda函数所使用以及传参的方式是传值还是传引用。 有哪些列表捕捉项呢 [变量名]表示传值捕捉某变量并传值传参传值捕捉来的变量不可被修改。 [变量名]以引用的方式捕捉某变量并传引用传参。注意不要与取地址混淆了只有在捕捉列表里是引用传递捕捉变量 []表示传值捕捉并传参包含 lambda表达式的代码块中的所有变量。包括 this []表示传引用捕捉并传参包含 lambda表达式的代码块中的所有变量。包括 this [this]表示值传递方式捕捉当前 this 指针。 2、参数列表 lambda 表达式的参数列表与普通函数的参数列表一致如果不需要传参可以连同括号一起省略。 3、mutable 在默认情况下 lambda函数中按值捕获的变量不可被修改。 添加 mutable 关键字可以取消 lambda 函数中按值捕获的变量的常量性使之可以被修改不想取消可以省略注意如果使用了该修饰符参数列表将不能省略 4、-返回值类型 采用追踪返回的形式声明函数返回值类型。如果没有返回值 / 返回值类型可以由编译器明确推导出来可以省略。 5、函数体 与普通函数的函数体一致必须要写。该函数体内除了可以使用参数列表中的参数还可以使用由捕捉列表捕捉到的变量 6、小总结 在 lambda 表达式的定义里(参数列表)、mutable、-返回值类型都是可以省略的部分而捕捉列表与函数体不能省略。因此最简单的 lambda 表达式就是[]{}但是没有任何意义 3、各种类型的 lambda 表达式代码示例 #include iostream using namespace std;int main() {// 1、最简单的lambda表达式[] {};// 2、省略参数列表mutable和返回值类型返回值类型由编译器推导出来int a 3, b 4;[] {return a b; };// 3、省略返回值类型无返回值类型和mutableauto fun1 [](int c) {return a c; }; // 注意lambda表达式必须要有变量来接收才能使用fun1(10); // 使用也是类似于函数cout a b endl;// 4、各方面都比较完善的lambda表达式auto fun2 [, b](int c)-int {return b (a c); };fun2(1);cout b endl;// 5、传值捕捉x添加mutable使之能被修改int x 10;auto fun3 [x](int a)mutable-int { a 2; x * 2; return x a; }; } 值得注意的是lambda表达式实际上可以被理解为无名函数不能直接调用想要直接调用必须通过 auto 赋值给一个变量。auto 也是C11更新的关键字用于自动推导类型 4、lambda表达式的一些注意事项  1、捕捉列表可由多个捕捉项组成并以逗号分割。 eg[, a, b]以引用传递的方式捕捉变量a和b值传递方式捕捉其他所有变量 [a, this]值传递方式捕捉变量a和this引用方式捕捉其他变量 2、捕捉列表不允许变量重复传递否则就会导致编译错误。 eg[, a]已经以值传递方式捕捉了所有变量捕捉a重复 3、在代码块作用域以外的lambda函数捕捉列表必须为空。 4、在块作用域中的lambda函数仅能捕捉父作用域中局部变量捕捉任何非此作用域或者 非局部变量都会导致编译报错。 5、lambda表达式之间不能相互赋值即使看起来类型相同 5、lambda表达式的底层原理 #include iostream using namespace std;class Rate { public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;} private:double _rate; };int main() {// 函数对象double rate 0.49;Rate r1(rate);r1(10000, 2);// lambda表达式auto r2 [](double monty, int year)-double {return monty * rate * year; };r2(10000, 2);return 0; } 之前说过从使用方式来看lambda表达式的使用方式与函数对象完全一样。 在这个代码里函数对象将rate作为其成员变量在定义对象时给出初始值即可lambda表达式通过捕获列表则可以直接将该变量捕获到。 其实底层编译器对 lambda 表达式的处理方式就是完全比照函数对象来的底层每一个lambda 表达式都会生成一个仿函数类。 可以发现C底层lambda表达式与仿函数的处理完全一致 五、常用包装器function  fuction 包装器又称适配器C中的function本质是一个类模板是为了解决模板低效问题而出现的可以统一类型 1、代码引入 #include iostream using namespace std;template class F, class TT useF(F f, T x) {static int count 0;cout count: count endl;cout count: count endl;return f(x); }double f(double i) {return i / 2; }struct Functor {double operator()(double d){return d / 3;} };int main() {// useF的模板参数做函数名cout useF(f, 10.27) endl;// useF的模板参数做仿函数cout useF(Functor(), 10.27) endl;// useF的模板参数做lambda表达式cout useF([](double d)-double {return d / 4; }, 10.27) endl;return 0; } 观察这段程序的运行结果我们可以发现一套模板可以调用的类型非常丰富函数指针、仿函数、lambda表达式。 但是他们的运行却是相互独立的每一种类型都会实例化出一份模板这会造成模板效率下降。 应该如何解决呢使用 function 包装器统一类型。 2、function 包装器的作用 函数指针、仿函数、lambda表达式这三大可调用对象各有一定的缺陷 函数指针用起来比较复杂苦涩而且局限性很大仿函数比较臃肿笨重而且要全局定义lambda 表达式又无法判断其类型只能依靠 auto 让编译器自己推导。 因此 function 包装器就出现了其作用就是把可调用对象给包装起来对类型进行统一同时解决模板低效问题。PS类成员函数也属于可调用对象也可以用function 将其包装起来但是 function 本身并不是可调用对象需要包装可调用对象才能使用 3、function 包装器的使用 前言使用std::function 需要包含头文件 functional 3.1  模板原型 template class T function;template class Ret, class ...Args class functionRet(Args...);/*模板参数说明Ret被调用对象的返回类型Args被调用对象的形参是个可变参数列表可以包装各式参数 */3.2  使用举例 #include iostream #include functional using namespace std;template class F, class T// 函数模板 T useF(F f, T x) {static int count 0;cout count: count endl;cout count: count endl;return f(x); }// 函数 double f(double i) {return i / 2; }// 仿函数 struct Functor {double operator()(double d){return d / 3;} };class Test { public:double minus(double x, double y){return x - y;}double add(double x, double y){return x y;} }; int main() {// 1、function包装函数名函数指针functiondouble(double) func1 f;cout useF(func1,10.27) endl;// 2、function包装仿函数functiondouble(double) func2 Functor();cout useF(func2, 10.27) endl;// 3、function包装lambda表达式functiondouble(double) func3 [](double x)-double {return x; };cout useF(func3, 10.27) endl;// 4、function包装类成员函数// 注意类成员函数的包装比较特殊可变参数包需要多一个this指针底层通过这个this指针调用函数包装的可调用函数也需要加类域 和 functiondouble(Test, double, double) func4 Test::add;cout func4(Test(), 20.15, 10.27) endl;return 0; } 观察运行结果可以发现函数模板只实例化了一份实现了类型统一。 六、通用函数适配器bind  std::bind 同样定义在 functional 头文件中就像一个函数适配器接收可调用对象一般与 function 搭配生成新的可调用对象来“适应”源对象参数列表多用来调节可调用对象参数的个数和顺序让可调用对象的参数使用更加灵活。 一般而言我们可以用 bind 把一个原本接收n个参数的函数通过绑定一些参数返回一个可以只接收其中部分参数的新函数。 1、模板原型 // 模板1不指定返回类型通用性较强编译器通过fn的类型自动推断 template class Fn, class ...Args bind(Fn fn, Args... args);// 模板2指定返回类型Ret使用的时候需要bindRet显式指明返回类型防止返回类型出错 template class Ret, class Fn, class ...Args bind(Fn fn, Args... args);/*参数类型说明fn万能引用传可调用对象args:可变参数包 */ 其中模板1是最常用的因此我们调用 bind 的一般形式是 auto NewCallable bind(callable, arg_list); 其中NewCallable 本身也需要是一个可调用对象arg_list 是一个逗号分隔的参数列表对应callable 中的参数。注意arg_liist 里面大概率会出现 _n n为正整数的占位符用来表示参数在 NewCallable 中的位置_1为newCallable的第一个参数_2为第二个参数以此类推 当我们调用NewCallable时NewCallable 会调用 callable并把arg_list 中的参数传给它。 2、使用举例重点 #include iostream #include functional using namespace std;int add(int x, int y) {return x y; }class Test { public:int minus(int x, int y){return x - y;} };int main() {// func1 绑定函数 add且参数由调用 func1, func2 的第一、二个参数指定functionint(int, int) func1 bind(add, placeholders::_1, placeholders:: _2);cout func1(2014, 2015) endl;// func2 也绑定函数add但是参数明确给出来func2 的类型由于两个参数全部写死类型实际上变成了functionint()functionint() func2 bind(add, 2014, 2015);cout func2() endl;// func3 绑定成员函数绑定成员函数需要有对应实例来调用同时需要取地址确认是哪个类的哪个成员函数不取编译器无法识别Test t;functionint(int, int) func3 bind(Test::minus, t, placeholders::_1, placeholders::_2);cout func3(2015, 2014) endl;// func4 改变参数的位置即func4的第一个参数由传递的第二个参数决定反之亦然functionint(int, int) func4 bind(Test::minus, t, placeholders::_2, placeholders::_1);cout func4(2015, 2014) endl;// func5 也可以调整传参的个数// 可以把每次都要固定传的参数给绑死在调用的时候简化调用。由于写死了一个形参所以function的类型也跟着改变为functionint(int)functionint(int) func5 bind(Test::minus, t, 2013, placeholders::_1);cout func5(2012) endl;return 0; } 七、C11标准线程库 在C11之前涉及到多线程问题都是和平台相关的比如在Windows和Linux下各有自己的多线程接口这使得代码的可移植性较差。  因此C11中最重要的特性就是提供了对线程的支持使得C在并行编程时不需要再依赖第三方库了。同时在原子操作中还引入了原子类的概念 使用C11标准线程库需要包含头文件 thread C使用手册中关于 thread 线程类的详细介绍 0、C标准线程库常用函数表格 thread()无参构造函数构造一个没有关联任何线程函数的线程对象不会启动任何线程thread(fn,args1,args2……构造出一个线程对象该对象关联了可执行对象 fnargs1、args2、……为线程函数的参数get_id()获取 thread 类型的线程ID joinable()判断线程是否还在执行如果线程对象关联了一个正在执行的线程返回 true 如果线程对象没有关联任何线程或者线程已经结束已经被回收返回 false  join()会使当前线程阻塞直到被调用的线程完全执行完毕子线程结束后还会自动回收子线程的资源用来保证线程同步detach() 在创建线程对象后马上调用用于把被创建线程与线程对象分离开分离 的线程变为后台线程创建的线程的死活就与主线程无关 1、标准线程库使用的一些注意事项 1、线程是操作系统中的一个概念线程对象可以关联一个线程用来控制线程和获取线程状态。 也因为线程对象的存在可以把线程以对象的形式往容器里面放 2、创建一个线程对象后如果没有提供线程函数该对象实际没有对应任何线程。 3、如果创建一个线程对象并且给该线程对象关联线程函数程序运行后该线程就会被启动与主线程一起并发运行。线程函数一般关联可调用对象函数指针、lambda表达式、函数对象 4、thread线程类是防拷贝的不允许拷贝构造以及赋值但是支持移动构造和移动赋值。即将一个线程对象关联的线程转移给其他线程对象转移期间不影响线程的执行 5、可以通过 jionable()函数来判断线程是否有效如果是以下任意情况则线程无效 采用无参构造函数构造的线程对象  线程对象的状态已经转移给其他线程对象  线程已经调用jion或者detach结束 2、线程函数的参数 线程函数在默认传参的时候是以传值方式传参的想要保留其引用特性需要使用 std::ref(参数名函数。当然传指针没有这种问题 (只要线程函数参数是左值引用传参的时候必须使用 ref() 函数把参数包装成引用才能保证左值引用正确传参——底层是先把参数传给 thread 线程类的构造函数再传给线程函数由于线程库底层某些复杂的原因所有的参数传过去都会变成右值。 #include iostream #include thread using namespace std;void ThreadFunc1(int x) {x 10; } void ThreadFunc2(int* x) {(*x) 10; }void ThreadFunc3(const string s) {cout s endl; }int main() {int x 2015;// 通过 ref 函数保持其引用属性否则会因为最后结果是右值导致报错没有对应函数thread t1(ThreadFunc1, ref(x));t1.join();cout x endl;// 传指针不存在此类问题thread t2(ThreadFunc2, x);t2.join();cout x endl;// 传右值就可以不会报错string s 二零一五;thread t3(ThreadFunc3, s);t3.join();return 0; } 3、线程函数的返回值 我们一般很难拿到线程函数的返回值如果想得到某种结果可以在线程函数中多加一个输出型参数用来承载结果。 4、原子性操作库atomic 多线程最主要的问题是共享数据带来的问题即线程安全问题。如果共享数据都是只读的那么没问 题因为只读操作不会影响到数据更不会涉及对数据的修改所以所有线程都会获得同样的数 据。但是当一个或多个线程要修改共享数据时就会产生很多潜在的问题。 那么应该如何解决线程安全问题呢 4.1  C98 线程安全解决方案对欲修改的共享数据加锁 4.1.1  互斥锁的类型 C 中有许多互斥锁类他们有各自的作用 C中互斥锁类的类型 mutex最常用的普通互斥锁recursive_mutex递归互斥锁在递归的时候使用因为一般的互斥锁在递归时会死锁底层简单来说就是在锁资源被占用后不会第一时间阻塞而是先判断是不是当前线程如果不是就不阻塞可以继续执行timed_mutex时间互斥锁可以设置超时时间相对/绝对一般用相对到了之后锁才可以加/解锁recursive_timed_mutex递归时间互斥锁 4.1.2  加锁解锁的函数 C加锁解锁的函数也有许多 lock()最基础且常用的阻塞式加锁如果锁资源被占用线程在此阻塞try_lock()防阻塞式锁先试一下能不能加锁判断一下锁资源有没有被占用如果没有就上锁不能字节返回不会向 lock()一样阻塞线程 try_lock_for(std::chrono::durationRep, Period rel_time) timed_mutex 时间互斥锁专属阻塞式锁在设置的相对超时时间只要写一下秒数即可因此时间互斥锁加锁常用这个而不是绝对超超时时间里尝试获取锁如果锁已被占用则阻塞当前线程直到超时时间到达或锁可用try_lock_until(const std::chrono::time_pointClock, Duration abs_time)timed_mutex 时间互斥锁专属阻塞式锁在设置的绝对超时时间即具体时间里尝试获取锁如果锁已被占用则阻塞当前线程直到超时时间到达或锁可用unlock()解锁 4.1.3  死锁问题 C中存在抛异常机制一旦发生抛异常会跳转到捕获异常处后面的代码不再执行这可能会导致锁资源没有被及时释放造成死锁 #include iostream #include thread #include mutex #include cstdlib // 包含 srand 和 rand #include ctime // 包含 timeusing namespace std;// 实现一个抛异常函数 void ThrowException() {if (rand() % 6 0){throw 异常;} }int main() {mutex mtx;int num 100; // 循环取随机数的次数// 初始化随机数生成器srand(static_castunsigned int(time(nullptr)));thread t1([num, mtx]() {try{for (int i 0; i num; i){mtx.lock();ThrowException();mtx.unlock();}}catch (const char* s){printf(这是线程1的异常: %s\n, s);}});thread t2([num, mtx]() {try{for (int i 0; i num; i){mtx.lock();ThrowException();mtx.unlock();}}catch (const char* s){printf(这是线程2的异常: %s\n, s);}});// 等待线程结束t1.join();t2.join();return 0; } 可以看到线程1先抢占了资源然后由于抛异常导致锁资源没有释放导致线程2一直阻塞造成死锁 那么应该如何解决呢这就要用到我们 C/C七中所提到的重要思想RAII思想了把资源交给一个类对象管理借助其析构函数进行解锁释放空间等操作。 #include iostream #include thread #include mutex #include cstdlib // 包含 srand 和 rand #include ctime // 包含 timeusing namespace std;// 实现一个抛异常函数 void ThrowException() {if (rand() % 6 0){throw 异常;} }// 利用RAII思想实现一个类用于托管资源 template class Lock class LockGuard { public:LockGuard(Lock mtx):_mtx(mtx){_mtx.lock();}~LockGuard(){_mtx.unlock();} private:Lock _mtx; //注意是引用类型并不创建新锁 }; int main() {mutex mtx;int num 100; // 循环取随机数的次数// 初始化随机数生成器srand(static_castunsigned int(time(nullptr)));thread t1([num, mtx]() {try{for (int i 0; i num; i){LockGuardmutex lg(mtx);ThrowException();}}catch (const char* s){printf(这是线程1的异常: %s\n, s);}});thread t2([num, mtx]() {try{for (int i 0; i num; i){LockGuardmutex lg(mtx);ThrowException();}}catch (const char* s){printf(这是线程2的异常: %s\n, s);}});// 等待线程结束t1.join();t2.join();return 0; } 这时候就可以正常运行了 PS这里的RAII资源托管类不需要手搓C11提供了两个模板类lock_guard 和 unique_lock 4.1.4  lock_guard VS unique_lock小重点 lock_guard模板类只支持构造函数和析构函数拷贝构造被禁止且使用方式很单一只能在构造函数和析构函数的时候进行加锁和解锁。 unique_lock模板类同样不支持拷贝构造函数但是支持手动加锁和解锁使用方式更加灵活提供了更多的成员函数。 某位前辈大佬整理的完整版 lock_guard 与 unique_lock 的区别 4.2  C11 提供的另一种线程安全解决方案原子操作 加锁虽然可以解决线程安全问题但是很多时候用的都是阻塞锁会影响程序的运行效率而且还可能会出现死锁问题。 所以 C11 就提供了原子操作即不可被中断的一个/一系列操作和原子类型必须包含头文件 atomic PS不过原子操作一般都是用在临界区较小操作较短的内置类型操作当中不需要加锁。线程可以直接对原子类型变量进行互斥地访问对于较长的操作 / 自定义类型的操作就不建议使用原子操作了因为原子操作通常会使用自旋锁spin lock这意味着线程在请求资源失败后会不断尝试获取资源而不是阻塞等待。如果操作时间较长这种自旋锁会导致 CPU 资源浪费 内置类型对应原子类型表大部分原子类型就是前面加个atomic_   #include iostream #include thread #include atomic using namespace std;void fun(atomiclong num, size_t count) {for (size_t i 0; i count; i){num; // 原子操作不需要加锁也能实现互斥访问} }int main() {atomiclong num 0;cout 原子操作前num num std::endl;thread t1(fun, ref(num), 1000000);thread t2(fun, ref(num), 1000000);t1.join();t2.join();cout 原子操作后, num num std::endl;return 0; }
http://www.w-s-a.com/news/996193/

相关文章:

  • 北京市建设工程信息网交易网站静态网页模板免费下载网站
  • 福田欧曼服务站网站前台设计
  • 网站做系统叫什么软件吗注册域名需要实名认证吗
  • jsp网站开发教学视频ui设计风格
  • 注册网站建设开发怎么自己做导航网站
  • 设计做网站品牌咖啡主题网页界面设计
  • 个人网站制作总体设计宿迁房价2023年最新房价
  • 服装网站建设进度及实施过程马鞍山网站设计制作
  • 郑州网站优化顾问济宁网站制作
  • 网站开发简单吗网站引导页分为三个板块设计风格
  • 湖南做网站 在线磐石网络百度一下百度搜索
  • 现在建网站多少钱推广营销费
  • 联想企业网站建设的思路西安网站建设阳建
  • 网站内容 内链网站建设电话销售工作总结
  • 系统网站开发知名的摄影网站有哪些
  • 网站拍照的幕布扬中网站建设价位
  • 网站ie兼容性差西安小程序开发的公司
  • 上海网站建设培训app网站开发成本
  • 个人网站icp外贸网站开发 河南
  • 遵义建设网站无锡市规划建设局网站
  • 海外留学网站建设方案门户网站的发布特点
  • 网站建设不赚钱net112企业建站系统
  • 网站建设团队管理模板贵州省住房和城乡建设部网站
  • 曲沃网站建设网上学编程的有哪些比较好的网站
  • 厦门网站建设慕枫学做网站需要多久
  • 爱奇艺做任务领vip网站设计广告图片
  • 中科汇联网站建设手册上海公司名称注册查询网
  • 网站建设电子商务课总结和体会关于做网站书籍
  • 仪征网站建设公司哪家好简单网页制作素材图片
  • 甘肃第九建设集团公司网站潍坊个人做网站