日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C++中的lambda表达式和线程库

發(fā)布時(shí)間:2023/11/30 c/c++ 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++中的lambda表达式和线程库 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

98中的一個(gè)例子

如果想要對(duì)一個(gè)數(shù)據(jù)集合中的元素進(jìn)行排序,可以使用std::sort方法

#include <algorithm> #include <functional> int main() {int array[] = {4,1,8,5,3,7,0,9,2,6};// 默認(rèn)按照小于比較,排出來(lái)結(jié)果是升序std::sort(array, array+sizeof(array)/sizeof(array[0]));// 如果需要降序,需要改變?cè)氐谋容^規(guī)則std::sort(array, array + sizeof(array) / sizeof(array[0]), greater<int>());return 0; }

排序一個(gè)單鏈表
如果待排序元素為自定義類(lèi)型,需要用戶(hù)定義排序時(shí)的比較規(guī)則:

struct Goods {string _name;double _price; }; struct Compare {bool operator()(const Goods& gl, const Goods& gr){return gl._price <= gr._price;} }; int main() {Goods gds[] = { { "蘋(píng)果", 2.1 }, { "相交", 3 }, { "橙子", 2.2 }, { "菠蘿", 1.5 } };sort(gds, gds + sizeof(gds) / sizeof(gds[0]), Compare());return 0; }

每次為了實(shí)現(xiàn)一個(gè)algorithm算法, 都要重新去寫(xiě)一個(gè)類(lèi),如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類(lèi),特別是相同類(lèi)的命名,這些都給編程者帶來(lái)了極大的不便

lambda表達(dá)式

函數(shù)中聲明函數(shù)

sort(gds, gds + sizeof(gds) / sizeof(gds[0]), [](const Goods&left, const Goods& right){return left._price < right._price; });

lambda表達(dá)式語(yǔ)法

lambda表達(dá)式書(shū)寫(xiě)格式:

  • [capture-list] (parameters) mutable -> return-type { statement }
  • lambda表達(dá)式各部分說(shuō)明
    • [capture-list] : 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開(kāi)始位置,編譯器根據(jù)[]來(lái)判斷接下來(lái)的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用。
    • (parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。
    • ->return-type:返回值類(lèi)型。用追蹤返回類(lèi)型形式聲明函數(shù)的返回值類(lèi)型,沒(méi)有返回值時(shí)此部分可省略。返回值類(lèi)型明確情況下,也可省略,由編譯器對(duì)返回類(lèi)型進(jìn)行推導(dǎo)。
    • {statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
  • [捕捉列表](參數(shù)列表)mutable->返回值類(lèi)型{//函數(shù)體}; []{}; //最簡(jiǎn)單的lambda表達(dá)式
  • 捕獲列表說(shuō)明
    捕捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
    • [var]:表示值傳遞方式捕捉變量var
    • [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
    • [&var]:表示引用傳遞捕捉變量var
    • [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
    • [this]:表示值傳遞方式捕捉當(dāng)前的this指針
  • 注意:

  • 父作用域指包含lambda函數(shù)的語(yǔ)句塊
  • 語(yǔ)法上捕捉列表可由多個(gè)捕捉項(xiàng)組成,并以逗號(hào)分割。
    比如:[=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量 [&,a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量 c. 捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯錯(cuò)誤。 比如:[=, a]:=已經(jīng)以值傳遞方式捕捉了所有變量,捕捉a重復(fù)
  • 在塊作用域以外的lambda函數(shù)捕捉列表必須為空。
  • 在塊作用域中的lambda函數(shù)僅能捕捉父作用域中局部變量,捕捉任何非此作用域或者非局部變量都會(huì)導(dǎo)致編譯報(bào)錯(cuò)。(主函數(shù)只能捕獲主函數(shù)聲明得變量)
  • int a = 3, b = 4; [=,&g_a]{return a + 3; }; //驗(yàn)證4
  • lambda表達(dá)式之間不能相互賦值,即使看起來(lái)類(lèi)型相同,找不到operator=()。
  • auto f1 = []{cout << "hello world" << endl; }; auto f2 = []{cout << "hello world" << endl; }; //f1 = f2; // 編譯失敗--->提示找不到operator=() // 允許使用一個(gè)lambda表達(dá)式拷貝構(gòu)造一個(gè)新的副本 auto f3(f2); f3(); // 可以將lambda表達(dá)式賦值給相同類(lèi)型的函數(shù)指針 PF = f2; PF();//執(zhí)行相應(yīng)得表達(dá)式 // 最簡(jiǎn)單的lambda表達(dá)式, 該lambda表達(dá)式?jīng)]有任何意義[]{};// 省略參數(shù)列表和返回值類(lèi)型,返回值類(lèi)型由編譯器推導(dǎo)為intint a = 3, b = 4;[=]{return a + 3; }; //想要在a的基礎(chǔ)上+3返回。//但是這個(gè)lambda表達(dá)式?jīng)]有用//因?yàn)闆](méi)有取名字// 省略了返回值類(lèi)型,無(wú)返回值類(lèi)型//不知道lambda表達(dá)式的類(lèi)型就用auto//[&]以引用的方式捕獲當(dāng)前主函數(shù)的變量 a=3;b=13;//[=]以值得方式進(jìn)行捕獲 a=3;b=4;auto fun1 = [=](int c)mutable{b = a + c; };fun1(10);cout << a << " " << b << endl;// 各部分都很完善的lambda函數(shù)// [=] 以值的方式捕獲所有的變量// [&] 以引用的方式捕獲所有的變量// [=,&b] 對(duì)于b變量以引用得方式捕獲,對(duì)于其它變量用值的方式auto fun2 = [=, &b](int c)->int{return b += a + c; };cout << fun2(10) << endl;// 復(fù)制捕捉x// 以值得方式捕獲x,函數(shù)內(nèi)修改不會(huì)影響外部int x = 10;auto add_x = [x](int a) mutable { x *= 2; return a + x; };cout << add_x(10) << endl;return 0;

    仿函數(shù)與lambda表達(dá)式的聯(lián)系

    函數(shù)對(duì)象,又稱(chēng)為仿函數(shù),即可以想函數(shù)一樣使用的對(duì)象,就是在類(lèi)中重載了operator()運(yùn)算符的類(lèi)對(duì)象

    class Rate { public:Rate(double rate) : _rate(rate){}double operator()(double money, int year){return money * _rate * year;} private:double _rate; }; int main() {// 函數(shù)對(duì)象double rate = 0.49;Rate r1(rate); //定義一個(gè)對(duì)象將利率傳進(jìn)去r1(10000, 2); //對(duì)象調(diào)用自身的方法,跟函數(shù)調(diào)用比較像都是 名字()// 仿函數(shù)//=捕獲rateauto r2 = [=](double monty, int year)->double{return monty*rate*year; };r2(10000, 2);return 0; }

    函數(shù)對(duì)象將rate作為其成員變量,在定義對(duì)象時(shí)給出初始值即可,lambda表達(dá)式通過(guò)捕獲列表可以直接將該變量捕獲到。
    實(shí)際在底層編譯器對(duì)于lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,即:如果定義了一個(gè)lambda表達(dá)式,編譯器會(huì)自動(dòng)生成一個(gè)類(lèi),在該類(lèi)中重載operator()。

    lambda表達(dá)式的應(yīng)用

    int array[] = { 1, 2, 3, 4, 5 }; for_each(array, array + 5, [](int&c){c *= 2; }); for_each(array, array + 5, [](int c){cout << c<<" "; });

    線程庫(kù)

    #include<thread> void ThreadFunc(int a) {cout << "Thread1" << a << endl; } class TF { public:void operator()(){cout << "Thread3" << endl;} };int main() {TF tf;//線程函數(shù)尾函數(shù)指針thread t1(ThreadFunc, 10);//線程函數(shù)為lambda表達(dá)式thread t2([]{cout << "Thread2" << endl; });//線程函數(shù)為函數(shù)對(duì)象thread t3(tf);t1.join();t2.join();t3.join();cout << "Main thread!" << endl;system("pause");return 0; }

    線程之間不能互相賦值,也不能拷貝

    線程的啟動(dòng)

    C++線程庫(kù)通過(guò)構(gòu)造一個(gè)線程對(duì)象來(lái)啟動(dòng)一個(gè)線程,該線程對(duì)象中就包含了線程運(yùn)行時(shí)的上下文環(huán)境,比如:線程函數(shù)、線程棧、線程起始狀態(tài)等以及線程ID等,所有操作全部封裝在一起,最后在底層統(tǒng)一傳遞給_beginthreadex() 創(chuàng)建線程函數(shù)來(lái)實(shí)現(xiàn) (注意_beginthreadex是windows中創(chuàng)建線程的底層c函數(shù))。std::thread()創(chuàng)建一個(gè)新的線程可以接受任意的可調(diào)對(duì)象類(lèi)型(帶參數(shù)或者不帶參數(shù)),包括lambda表達(dá)式(帶變量捕獲或者不帶),函數(shù),函數(shù)對(duì)象,以及函數(shù)指針。

    #include<thread> void ThreadFunc1(int& x) {cout << &x << " " << x << endl;x += 10; }void ThreadFunc2(int*x) {*x += 10; }int main() {int a = 10;//在線程函數(shù)中對(duì)a修改,不會(huì)影響外部實(shí)參,因?yàn)?線程函數(shù)雖然是引用方式,但其實(shí)際引用的是線程棧中的拷貝thread t1(ThreadFunc1, a);t1.join();cout << &a <<" "<< a << endl;//地址的拷貝thread t3(ThreadFunc2, &a);t3.join();cout << a << endl;system("pause");return 0; }

    線程的結(jié)束

    1. join()方式

    join():會(huì)主動(dòng)地等待線程的終止。在調(diào)用進(jìn)程中join(),當(dāng)新的線程終止時(shí),join()會(huì)清理相關(guān)的資源,然后返回,調(diào)用線程再繼續(xù)向下執(zhí)行。由于join()清理了線程的相關(guān)資源,thread對(duì)象與已銷(xiāo)毀的線程就沒(méi)有關(guān)系了,因此一個(gè)線程的對(duì)象每次你只能使用一次join(),當(dāng)你調(diào)用的join()之后joinable()就將返回false了。主線程會(huì)阻塞

    2. detach()

    detach:會(huì)從調(diào)用線程中分理出新的線程,之后不能再與新線程交互。就像是你和你女朋友分手,那之后你們就不會(huì)再有聯(lián)系(交互)了,而她的之后消費(fèi)的各種資源也就不需要你去埋單了(清理資源)。此時(shí)調(diào)用joinable()必然是返回false。分離的線程會(huì)在后臺(tái)運(yùn)行,其所有權(quán)和控制權(quán)將會(huì)交給c++運(yùn)行庫(kù)。同時(shí),C++運(yùn)行庫(kù)保證,當(dāng)線程退出時(shí),其相關(guān)資源的能夠正確的回收

    原子性操作

    多線程最主要的問(wèn)題是共享數(shù)據(jù)帶來(lái)的問(wèn)題(即線程安全)。如果共享數(shù)據(jù)都是只讀的,那么沒(méi)問(wèn)題,因?yàn)橹蛔x操作不會(huì)影響到數(shù)據(jù),更不會(huì)涉及對(duì)數(shù)據(jù)的修改,所以所有線程都會(huì)獲得同樣的數(shù)據(jù)。但是,當(dāng)一個(gè)或多個(gè)線程要修改共享數(shù)據(jù)時(shí),就會(huì)產(chǎn)生很多潛在的麻煩。比如:

    #include <iostream> using namespace std; #include <thread> unsigned long sum = 0L; void fun(size_t num) {for (size_t i = 0; i < num; ++i)sum++; } int main() {cout << "Before joining,sum = " << sum << std::endl;thread t1(fun, 10000000);thread t2(fun, 10000000);//兩個(gè)線程每個(gè)每回都循環(huán)10000000次,每次加1//如果沒(méi)問(wèn)題應(yīng)該是20000000t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;system("pause");return 0; }

    通過(guò)加鎖保證線程安全

    #include <iostream> using namespace std; #include <thread> #include<mutex> unsigned long sum = 0L;mutex m; void fun(size_t num) {for (size_t i = 0; i < num; ++i){m.lock();sum++;m.unlock();} } int main() {size_t begin = clock();cout << "Before joining,sum = " << sum << std::endl;thread t1(fun, 10000000);thread t2(fun, 10000000);//兩個(gè)線程每個(gè)每回都循環(huán)10000000次,每次加1//如果沒(méi)問(wèn)題應(yīng)該是20000000t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;size_t end = clock();cout << end - begin << endl; //計(jì)算時(shí)間system("pause");return 0; }

    加鎖后,能夠保證線程安全,但是耗費(fèi)的時(shí)間就比較多,而且有可能導(dǎo)致死鎖

    原子操作

    原子操作:一但開(kāi)始,不能被打斷

    對(duì)于內(nèi)置類(lèi)型

    對(duì)于自定義類(lèi)型

    使用atomic模板,定義出需要的任意原子類(lèi)型

    atomic<T> t;

    注意事項(xiàng)

    原子類(lèi)型通常屬于“資源型”數(shù)據(jù),多個(gè)線程只能訪問(wèn)單個(gè)原子類(lèi)型的拷貝,因此在c++11中原子類(lèi)型只能從其模板參數(shù)中進(jìn)行構(gòu)造,不允許原子類(lèi)型進(jìn)行拷貝構(gòu)造,移動(dòng)構(gòu)造以及operator=等,于是,標(biāo)準(zhǔn)庫(kù)已經(jīng)將atmoic模板類(lèi)中的拷貝構(gòu)造,移動(dòng)構(gòu)造,賦值運(yùn)算符的重載默認(rèn)刪除了

    #include <iostream> using namespace std; #include <thread> #include<atomic>//unsigned long sum = 0L; atomic_long sum{0}; //定義原子類(lèi)型變量void fun(size_t num) {for (size_t i = 0; i < num; ++i){sum++;} } int main() {size_t begin = clock();cout << "Before joining,sum = " << sum << std::endl;thread t1(fun, 10000000);thread t2(fun, 10000000);//兩個(gè)線程每個(gè)每回都循環(huán)10000000次,每次加1//如果沒(méi)問(wèn)題應(yīng)該是20000000t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;size_t end = clock();cout << end - begin << endl; //計(jì)算時(shí)間system("pause");return 0; }


    時(shí)間更短,也能保證線程安全

    總結(jié)

    以上是生活随笔為你收集整理的C++中的lambda表达式和线程库的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。