6-9 async、future、packaged_task、pro

一: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;
}

四:小結:到底怎麼用,什麼時候用;

我們學習這些東西的目的,並不是要把他們都用在咱們自己的實際開發中。
相反,如果我們能夠用最少的東西能夠寫出一個穩定、高效的多線程程序,更值得讚賞;
我們爲了成長,必須要閱讀一些高手些的代碼,從而快速實現自己代碼的積累,我們的技術就會有一個大幅度的提升;
爲我們將來能讀懂高手代碼鋪路。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章