C++中的lambda表达式和线程库
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)按照小于比較,排出來結(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è)單鏈表
如果待排序元素為自定義類型,需要用戶定義排序時(shí)的比較規(guī)則:
每次為了實(shí)現(xiàn)一個(gè)algorithm算法, 都要重新去寫一個(gè)類,如果每次比較的邏輯不一樣,還要去實(shí)現(xiàn)多個(gè)類,特別是相同類的命名,這些都給編程者帶來了極大的不便
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á)式語法
lambda表達(dá)式書寫格式:
- [capture-list] (parameters) mutable -> return-type { statement }
- [capture-list] : 捕捉列表,該列表總是出現(xiàn)在lambda函數(shù)的開始位置,編譯器根據(jù)[]來判斷接下來的代碼是否為lambda函數(shù),捕捉列表能夠捕捉上下文中的變量供lambda函數(shù)使用。
- (parameters):參數(shù)列表。與普通函數(shù)的參數(shù)列表一致,如果不需要參數(shù)傳遞,則可以連同()一起省略mutable:默認(rèn)情況下,lambda函數(shù)總是一個(gè)const函數(shù),mutable可以取消其常量性。使用該修飾符時(shí),參數(shù)列表不可省略(即使參數(shù)為空)。
- ->return-type:返回值類型。用追蹤返回類型形式聲明函數(shù)的返回值類型,沒有返回值時(shí)此部分可省略。返回值類型明確情況下,也可省略,由編譯器對(duì)返回類型進(jìn)行推導(dǎo)。
- {statement}:函數(shù)體。在該函數(shù)體內(nèi),除了可以使用其參數(shù)外,還可以使用所有捕獲到的變量。
捕捉列表描述了上下文中那些數(shù)據(jù)可以被lambda使用,以及使用的方式傳值還是傳引用。
- [var]:表示值傳遞方式捕捉變量var
- [=]:表示值傳遞方式捕獲所有父作用域中的變量(包括this)
- [&var]:表示引用傳遞捕捉變量var
- [&]:表示引用傳遞捕捉所有父作用域中的變量(包括this)
- [this]:表示值傳遞方式捕捉當(dāng)前的this指針
注意:
比如:[=, &a, &b]:以引用傳遞的方式捕捉變量a和b,值傳遞方式捕捉其他所有變量 [&,a, this]:值傳遞方式捕捉變量a和this,引用方式捕捉其他變量 c. 捕捉列表不允許變量重復(fù)傳遞,否則就會(huì)導(dǎo)致編譯錯(cuò)誤。 比如:[=, a]:=已經(jīng)以值傳遞方式捕捉了所有變量,捕捉a重復(fù)
仿函數(shù)與lambda表達(dá)式的聯(lián)系
函數(shù)對(duì)象,又稱為仿函數(shù),即可以想函數(shù)一樣使用的對(duì)象,就是在類中重載了operator()運(yùn)算符的類對(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á)式通過捕獲列表可以直接將該變量捕獲到。
實(shí)際在底層編譯器對(duì)于lambda表達(dá)式的處理方式,完全就是按照函數(shù)對(duì)象的方式處理的,即:如果定義了一個(gè)lambda表達(dá)式,編譯器會(huì)自動(dòng)生成一個(gè)類,在該類中重載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ù)通過構(gòu)造一個(gè)線程對(duì)象來啟動(dòng)一個(gè)線程,該線程對(duì)象中就包含了線程運(yùn)行時(shí)的上下文環(huán)境,比如:線程函數(shù)、線程棧、線程起始狀態(tài)等以及線程ID等,所有操作全部封裝在一起,最后在底層統(tǒng)一傳遞給_beginthreadex() 創(chuàng)建線程函數(shù)來實(shí)現(xiàn) (注意_beginthreadex是windows中創(chuàng)建線程的底層c函數(shù))。std::thread()創(chuàng)建一個(gè)新的線程可以接受任意的可調(diào)對(duì)象類型(帶參數(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ì)象與已銷毀的線程就沒有關(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)資源的能夠正確的回收
原子性操作
多線程最主要的問題是共享數(shù)據(jù)帶來的問題(即線程安全)。如果共享數(shù)據(jù)都是只讀的,那么沒問題,因?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//如果沒問題應(yīng)該是20000000t1.join();t2.join();cout << "After joining,sum = " << sum << std::endl;system("pause");return 0; }通過加鎖保證線程安全
#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//如果沒問題應(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)致死鎖
原子操作
原子操作:一但開始,不能被打斷
對(duì)于內(nèi)置類型
對(duì)于自定義類型
使用atomic模板,定義出需要的任意原子類型
atomic<T> t;注意事項(xiàng)
原子類型通常屬于“資源型”數(shù)據(jù),多個(gè)線程只能訪問單個(gè)原子類型的拷貝,因此在c++11中原子類型只能從其模板參數(shù)中進(jìn)行構(gòu)造,不允許原子類型進(jìn)行拷貝構(gòu)造,移動(dòng)構(gòu)造以及operator=等,于是,標(biāo)準(zhǔn)庫(kù)已經(jīng)將atmoic模板類中的拷貝構(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}; //定義原子類型變量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//如果沒問題應(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)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 输卵管积水有成功的没
- 下一篇: 联想G5005能拖8T硬盘吗?