C++日記——Day44:async、future、packaged_task、promise

std::async、std::future創建後臺任務並返回值

std::async是一個函數模板,用來啓動一個異步任務,啓動起來一個異步任務之後,它返回一個std::future對象,這個對象是個類模板。

什麼叫“啓動一個異步任務”?就是自動創建一個線程,並開始 執行對應的線程入口函數,它返回一個std::future對象,這個std::future對象中就含有線程入口函數所返回的結果,我們可以通過調用future對象的成員函數get()來獲取結果。

“future”將來的意思,也有人稱呼std::future提供了一種訪問異步操作結果的機制,就是說這個結果你可能沒辦法馬上拿到,但是在不久的將來,這個線程執行完畢的時候,你就能夠拿到結果了,所以,大家這麼理解:future中保存着一個值,這個值是在將來的某個時刻能夠拿到。

std::future對象的get()成員函數會等待線程執行結束並返回結果,拿不到結果它就會一直等待,感覺有點像join()但是,它是可以獲取結果的。

wait()函數,用於等待線程返回,本身並不返回結果,這個效果和 std::thread 的join()更像。

#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;
class A {
public:
	int mythread(int mypar) {
		cout << mypar << endl;
		return mypar;
	}
};


int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}


int main() {
	A a;
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在這裏等待mythread()執行完畢,拿到結果
	
	//類成員函數
	std::future<int> result2 = std::async(&A::mythread, &a, tmp); //第二個參數是對象引用才能保證線程裏執行的是同一個對象
	cout << result2.get() << endl;
	cout << "good luck" << endl;
	return 0;
}

我們通過向std::async()傳遞一個參數,改參數是std::launch類型(枚舉類型),來達到一些特殊的目的:

1、std::lunch::deferred:表示線程入口函數調用被延遲到,std::future的wait()或者get()函數調用時才執行;如果wait()或者get()沒有被調用,則不會執行。實際上根本就沒有創建。(實際上延遲調用,並沒有創建新線程,是在主線程中調用的線程入口函數)。

#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;

int mythread() {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}


int main() {
	A a;
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(std::launch::deferred ,mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; //卡在這裏等待mythread()執行完畢,拿到結果
	cout << "good luck" << endl;
	return 0;
}

2、std::launch::async,再調用async函數的時候就開始創建線程。async()這個函數默認用的就是std::launch::async標記

int main() {
	A a;
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::future<int> result1 = std::async(std::launch::async ,mythread);
	cout << "continue........" << endl;
	cout << result1.get() << endl; 
	cout << "good luck" << endl;
	return 0;
}

 

std::packaged_task:打包任務,把任務包裝起來。

類模板,它的模板參數是各種課調用對象,通過packaged_task把各種可調用對象包裝起來,方便將來作爲線程入口函數。

#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;

int mythread(int mypar) {
	cout << mypar << endl;
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	return 5;
}


int main() {
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_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 << "good luck" << endl;
	return 0;
}

std::future對象裏包含有線程入口函數的返回結果,這裏result保存mythread返回的結果。

 

lambda表達式

int main() {
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return 5;
	}); 
	
	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 << "good luck" << endl;
	return 0;
}

packaged_task包裝起來的可調用對象還可以直接調用,從這個角度來講,packaged_task對象也是一個可調用對象

 

lambda的直接調用

int main() {
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return 5;
	}); 

	mypt(105);
	std::future<int> result = mypt.get_future();
	cout << result.get() << endl;
}
#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;
vector<std::packaged_task<int(int)>> task_vec;

int main() {
	int tmp = 12;
	cout << "main" << "threadid = " << std::this_thread::get_id() << endl;
	std::packaged_task<int(int)> mypt([](int mypar) {
		cout << mypar << endl;
		cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
		std::chrono::milliseconds dura(5000);
		std::this_thread::sleep_for(dura);
		cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
		return 5;
	}); 

	task_vec.push_back(std::move(mypt)); //入容器,注意這裏用的是移動語義,而不是拷貝
}

 

std::promise,類模板

我們能夠在某個線程中給它賦值,然後我們可以在其他線程中,把這個值取出來

#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;

void mythread(std::promise<int> &tmp, int clac) {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;
	tmp.set_value(result); //結果保存到了tmp這個對象中
	return;
}

vector<std::packaged_task<int(int)>> task_vec;

int main() {
	std::promise<int> myprom;
	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;
}

總結:通過promise保存一個值,在將來某個時刻我們通過吧一個future綁定到這個promise上,來得到綁定的值

注意:使用thread時,必須 join() 或者 detach() 否則程序會報異常

#include <thread>
#include <iostream>
#include <list>
#include <map>
#include <mutex>
#include <future>
using namespace std;

void mythread(std::promise<int> &tmp, int clac) {
	cout << "mythread() start" << "threadid = " << std::this_thread::get_id() << endl;
	std::chrono::milliseconds dura(5000);
	std::this_thread::sleep_for(dura);
	cout << "mythread() end" << "threadid = " << std::this_thread::get_id() << endl;
	int result = clac;
	tmp.set_value(result); //結果保存到了tmp這個對象中
	return;
}

void mythread2(std::future<int> &tmpf) {
	auto result = tmpf.get();
	return;
}

vector<std::packaged_task<int(int)>> task_vec;

int main() {
	std::promise<int> myprom;
	std::thread t1(mythread, std::ref(myprom), 180);
	t1.join(); //在這裏線程已經執行完了
	std::future<int> fu1 = myprom.get_future(); //promise和future綁定,用於獲取線程返回值
	std::thread t2(mythread2, std::ref(fu1));
	t2.join();
	return 0;
}

小結:

我們學習這些東西的目的並不是,要把他們都用到實際開發中。

相反,如果我們能夠用最少的東西寫出一個穩定的,高效的多線程程序,更值得讚賞。

我們爲了成長必須閱讀一些高手寫的代碼,從而實現自己代碼的積累;

 

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