一:std::async、std::future創建後臺任務並返回值
希望線程返回一個結果;
std::async是個函數模板,用來啓動一個異步任務,啓動起來一個異步任務之後,他返回一個std::future對象,std::future是個類模板。
什麼叫”啓動一個異步任務“,就是自動創建一個線程並開始執行對應的線程入口函數,它返回一個std::future對象,這個std::future對象裏邊就含有線程入口函數所返回的結果(線程返回的結果);我們可以通過調用future對象的成員函數get()來獲取。
future:將來的意思,有人也稱呼std::future提供了一種訪問異步操作結果的機制,就是說這個結果你可能沒有辦法馬上獲取。
在線程執行完畢的時候,你就能夠拿到結果了,所以大家就這麼理解:這個future(對象)裏會保存一個值,在將來的某個時刻獲取到.
int mythread() //線程入口函數
{
cout<<"mythread() start"<<"threadid="<<std::this_thread::get_id()<<endl;//打印新線程 id
std::chrono::milliseconds dura(5000);//頂一個5秒的時間
std::this_thread::sleep_for(dura);//休息了一定時長
cout<<"mythread() end"<<"threadid="<<std::this_thread::get_id()<<endl;//打印新線程id
return 5;
}
//下列程序通過std::future對象的get()成員函數等待線程執行結束並返回結果;
//這個get()函數很牛,不拿到將來的返回值誓不罷休,不拿到值我就卡在這裏等待拿值;
//std::thread
void main()
{
//我們通過額外向std::async()傳遞一個參數,該參數類型是std::lunch類型(枚舉類型),來達到一些特殊的目的。
//std::launch::deferred:表示線程入口函數調用被延遲到std:future的wait()或get()函數調用時才執行;
//那如果wait()或者get()沒有被調用那麼線程會執行嗎?沒執行。實際上,線程根本就沒創建
//std::launch::deferred:延遲調用,並且沒有創建新線程,是在主線程中調用的線程入口函數;
//b、std::launch::async,在調用async函數的時候就開始創建線程;
//async()函數,默認用的是async|deferred標記,看CPU利用率
A a;
int tmppar = 12;
cout<<"main"<<"threadid="<<std::this_thread::get_id()<<endl;
std::future<int> result = std::async(mythread); //創建一個線程並開始執行
std::future<int> result = std::async(std::launch::deferred,&a::mythread,&a,tmppar);// 第二個參數是對象引用才能保證線程裏用的是同一個對象
cout<<"continue....!"<<endl;
int def;
def = 0;
cout<<result.get()<<endl;//卡在這裏等待mythread()執行完畢,拿到結果
//get只能調用一次,不能調用多次。
//result.wait(); //等待線程返回,本身並不返回結果;
cout<<"I Love China!"<<endl;
return 0;
}
二:std::package_tack: 打包任務,把任務包裝起來
是個類模板,它的模板參數是各種可調用對象;通過std::package_task來把各種可調用對象包裝起來,方便將來作爲線程入口函數
std::packaged_task<int(int)> mypt([](int mypar){
cout<<"mythread() start"<<"threadid="<<std::this_thread::get_id()<<endl;//打印新線程 id
std::chrono::milliseconds dura(5000);//頂一個5秒的時間
std::this_thread::sleep_for(dura);//休息了一定時長
cout<<"mythread() end"<<"threadid="<<std::this_thread::get_id()<<endl;//打印新線程id
return 5;
}
cout<<"main"<<"threadid="<<std::this_thread::get_id()<<endl;
std::package_task<int(int)> mypt(mythread); //我們把函數mythread通過packaged_task包裝起來
std::thread t1(std::ref(mypt),1); //線程直接開始執行,第二個參數作爲線程入口函數的參數。
t1.join();//等待線程執行完畢。
std::future<int> result = mypt.get_future(); //std::future對象裏包含有線程入口函數的返回結果,這裏result保存mythread返回值
cout<<result.get()<<endl;
cout<<"I Love China!"<<endl;
mytasks.push_back(std::move(mypt)); //入容器,這裏用了移動語義,入進去之後mypt就爲空
//...
std::packaged_task<int(int)> mypt2;
auto iter = mytasks.begin();
mypt2 = std::move(*iter);//移動語義
mytasks.erase(iter); //刪除第一個元素,迭代已經失效了,所以後續代碼不可以再使用iter
mypt2(123);
std::future<int> result = mypt2.get_future();
cout<<result.get()<<endl;
三:std::promise類模板
我們能夠在某個線程中給它賦值,然後我們可以在其他線程中,把這個值取出來用;
void mythread(std::promise<int> &tmpp,int calc) //大家注意第一個參數
{
//做一系列複雜的操作
calc++;
calc *= 10;
//做其他運算,比如整整花費了5秒鐘;
std::chrono::milliseconds dura(5000); //頂一個5秒的時間
std::this_thread::sleep_for(dura); //休息了一定時長
//終於計算出結果了
int result = calc; //保存結果
tmpp.set_value(result); //結果我保存到了tmpp這個對象中;
}
void mythread2(std::future<int> & tmpf)
{
auto result = tmpf.get();
cout<<"mythread2 result"<<result<<endl;
return;
}
void main()
{
//std::promise<int> myprom; //聲明一個std::promise對象myprom,保存的值類型爲int;
std::thread t1(mythread,std::ref(myprom),180);
t1.join();
//獲取結果值
std::future<int> fu1 = myprom.get_future(); //promise和future綁定,用於獲取線程返回值
//auto result = fu1.get();
//cout<<"result = "<<result<<endl;
std::thread t2(mythread2, std::ref(fu1));
t2.join();//等mythread2線程執行完畢
cout<<"I Love China!"<<endl;
}
四:小結:到底怎麼用,什麼時候用;
我們學習這些東西的目的,並不是要把他們都用在咱們自己的實際開發中。
相反,如果我們能夠用最少的東西能夠寫出一個穩定、高效的多線程程序,更值得讚賞;
我們爲了成長,必須要閱讀一些高手些的代碼,從而快速實現自己代碼的積累,我們的技術就會有一個大幅度的提升;
爲我們將來能讀懂高手代碼鋪路。