Multi thread: std::async()和std::future(1)

對於初學者而言,「以多線程運行程序」的最佳起點就是C++標準庫中的 std::async()class std::future提供的高級接口.ios

(1),std::async()提供一個接口,讓一段機能或者說一個callable object function(可調用函數對象)如果可能的話在後臺運行成爲一個獨立的線程.c++

(2),std::future容許你等待線程結束並獲取其結果(一個返回值,也有多是一個異常).多線程

#include <iostream>
#include <future>
#include <chrono>
#include <random>
#include <exception>

int doSomething(const char& c)
{
	std::default_random_engine engine(c);
	std::uniform_int_distribution<int> id(10, 1000);
	
	for(int i=0; i<10; ++i){
		std::this_thread::sleep_for(std::chrono::milliseconds(id(engine)));
		std::cout.put(c).flush();
	}
	
	return static_cast<int>(c);
}

int func1()
{
	return doSomething(',');
}

int func2()
{
	return doSomething('+');
}

int main()
{
	std::cout<< "start func1() in background"
	         << "and func2() in foreground: "<<std::endl;
	
	//開始執行func1,也許是當即被放到後臺,也許是待會放到後臺,也許永遠不會放到後臺.     
	std::future<int> result1(std::async(func1, std::launch::async | std::launch::deferred)); //經過std::async(func1)啓動func1並嘗試把他放到後臺. 
	
	int result2 = func2();
	int result = result1.get() + result2;
	
	std::cout<< "\nresult of func1()+func2(): "<<result<<std::endl;
	
	return 0;
}

上面的代碼中咱們使用了:dom

std::future<int> result1(std::async(func1));這裏,理論上std::async()嘗試將其所得到的函數馬上異步啓動於一個獨立線程內。所以概念上func1()在這裏就被啓動了,不會形成main線程被阻塞(block).異步

可是須要注意的是:async

1,雖然std::future能讓你取得傳給std::async()的那個callable object」的返回值(若是那個callable object執行正常那麼得到的是返回值,不然是個異常)。函數

2,std::future確保callable object會被調用,注意先前說的std::async()嘗試啓動目標函數.若是callable object並無被當即啓動,稍後咱們須要這個future object才能強迫執行它(但咱們須要函數的運行結果或當咱們想要確保該函數被執行時).this

 

結合上面的代碼(特別須要注意的是: std::launch::async|std::launch::deferred):spa

int result = result1.get() + result2; 隨着get()調用,如下三件事之一可能會發生:線程

1,若是func1()被async()啓動且已經執行結束,會馬上得到其結果.

2,若是func1()被啓動可是還沒有結束,get()會引起main線程阻塞(block)直至func1()結束後得到結果.

3,若是func1()還沒有啓動,會強迫啓動func1()在一個獨立的線程且引發main線程(block)直到func1()執行完畢.

std::future<int> result1(std::async(func1));

result1.get();

這樣的組合帶來的好處就是:

1,若是可能,當main線程處理到std::future<int> result1(std::async(func1))時func1()可能被並行運行.

2,若是沒法並行運行,那麼會在執行到 result1.get()時被串行(serial)調用,這就意味着不管如何都能保證func1()被調用!.

 

(2), std::async()

template< class Function, class... Args>
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>>
    async( Function&& f, Args&&... args );



template< class Function, class... Args >
[[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, std::decay_t<Args>...>>
    async( std::launch policy, Function&& f, Args&&... args );

 

其實就是一個template function,返回一個std::future.該函數可接受三種類型的參數:

1) std::launch: 

   1,std::launch::async 它會告訴std::async()當即以並行的方式啓動callable object。若是異步調用在此沒法實現,程序會拋出一個std::system_error的異常.

 2,std::launch::deferred 它告訴std::async()對目標函數進行延緩執行,這保證目標函數絕對不會在沒有get()和wait()調用的被啓用,這也就意味着再也不是單獨的使用一個線程了,也一樣是在main線程中執行.

3,std::lanuch::async | std::lanuch::deferred 它告訴std::async()啓用目標函數的時候自動選擇policy.

 2) callable object

 1,能夠是函數

 2,能夠是重載了operator()的struct/class.

3)Arags&&...

1,一個或多個參數(能夠不一樣類型)做爲callable object的參數(parameters).

4),[nodiscard]

這個是c++11以來的新特性,前綴這個意味着咱們不能忽略該函數的返回值.

具體參閱這裏:http://en.cppreference.com/w/cpp/language/attributes

demo1: lazy求值:

好比下面的例子,不用每次都求出task1和task2的返回值.只須要求出一個咱們須要返回值就能夠了.

auto f1 = std::async(std::launch::deferred, task1);
auto f2 = std::async(std::launch::deferred, task2);
.........
auto val = jundge() ? f1.get() : f2.get();

 

(3) std::future

咱們接着來看一下std::future:

1)

template <class T>  future;
template <class R&> future<R&>;     // 特例化 : T is a reference type (R&)
template <>         future<void>;   // 特例化 : T is void

從上面咱們能夠看出來class std::future<>針對引用類型和void類型進行了特例化.

2)constructor

future() noexcept;
future (const future&) = delete;	
future (future&& x) noexcept;

class std::future<>的拷貝構造函數是被刪除了的,只能被移動構造.

 

3) member function(s)

std::future::get();

T get();
R& future<R&>::get();       // 特例化版本: when T is a reference type (R&)
void future<void>::get();   // 特例化版本: when T is void

一個std::future只能調用一次get();在調用了get()以後當前class std::future<>就會處於無效狀態,該class std::future的狀態只能經過valid()來檢測.

1,若是callable object被async()啓動(不管是並行仍是串行調用)且已經執行結束,會馬上得到其結果.

2,若是callable object被啓動可是還沒有結束:

  若是,咱們指定的策略爲std::launch::async,這個時候callable object在另一個線程中執行,所以會阻塞main線程直到,該callable object所在的線程執行完成.

  若是,咱們指定的策略爲std::launch::deferred,那麼此時callable object會在當前main線程中執行(其實就是串行辣).

 若是,指定的策略爲std::launch::async | std::launch::deferred,那麼取決於編譯器,執行狀況會是上面的兩種.

 

std::future::valid()

bool valid() const noexcept;

若是當前std::future object沒有調用過std::future::get()返回true,不然返回false.

 

std::future::wait();

void wait() const;

在std::future中有個shared state默認狀況下是not ready:

若是當前std::future對象調用wait()的時候shared state處於未not ready狀態,那麼wait()就會阻塞(block)正在調用wait()的線程直到callable object執行完成,且把sheared state設置爲ready這樣就意味着咱們在下面的代碼中直接調用get()就會直接得到callable object的返回值.

demo 2 for wait:

#include <iostream>
#include <chrono>
#include <future>


bool is_prime(const int& number)
{
	for(int i=2; i<number; ++i){
		if(number%i == 0){
			return false;
			
		}
	}
	
	return true;
}

int main()
{
	std::future<bool> result(std::async(is_prime, 194232491));
	
	std::cout<<"check...."<<std::endl;
	result.wait(); //線程在這裏停頓了一下. 
	
	std::cout<<"194232491\n";
	if(result.get()){ //這裏則是直接出來告終果. 
		std::cout<<"is prime"<<std::endl;
		
	}else{
		std::cout<<"is not prime"<<std::endl;
	}
	
	return 0;
}

std::future::wait_until() 和 std::future::wait_for()

在瞭解這兩個函數以前咱們須要瞭解一下std::future的三種狀態:

std::future_status::ready  ------------ std::future中的shared state已經被設置爲ready,且callable object已經被產生了一個值或者異常.

std::future_satus::timeout  ------- --- std::future中的shared state在指定的時間段(std::chrono::duration)或者到了指定的時間點(std::chrono::time_point)尚未被設置爲ready.(異步操做超時)

std::future_status::defferred -------------若是std::async()延緩了callable object的調用然後續的程序中又徹底沒調用wait()或者get(),那麼wait_until()和wait_for()就會返回這個.

#include <iostream>
#include <chrono>
#include <future>

bool is_prime(const int& number)
{
	for(int i=2; i<number; ++i){
		if(number%i == 0){
			return false;
		}
	}
	
	return true;
}

int main()
{
	std::future<bool> result(std::async(is_prime, 700020007));
	
	std::cout<<"checking please waiting"<<std::endl;
	std::chrono::milliseconds span(100);
	
	while(result.wait_for(span) == std::future_status::timeout){ //檢查是否超時. 
    //可是請注意上這裏的循環可能永遠不會結束,由於可能在單線程的環境中,將被推遲到get()被調用的時候,才調用callable object.
    //所以若是調用std::async()而且給他指定了launch類型爲std::launch::async;或者沒有指定launch類型
    //最好使用result.wait_for(span) == std::future_status::ready或者 std::future_status::deferred
		std::cout<<".";
    }
    
	bool x = result.get();
	std::cout << "\n700020007 " << (x?"is":"is not") << " prime.\n";
	
	return 0;
}

std::future<>::wait_for()

template <class Rep, class Period>
  future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

阻塞調用wait_for()的線程rel_time時間段等待std::future的shared state被設置被設置爲ready(若是此時callable object已經在後臺調用完成那麼返回std::future_status::ready)不然返回std::future_status::timeout,可是若是在std::async()調用了callable object的時候使用了std::launch::deferred(推遲調用callable object),wait_for()是不會強制啓動對callable object的調用的,也會返回一個std::future_status的狀態.

// future::wait_for
#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <chrono>         // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool is_prime(int x) {
	for (int i = 2; i<x; ++i) if (x%i == 0) return false;
	return true;
}

int main()
{
	// call function asynchronously:
	std::future<bool> fut = std::async(is_prime, 700020007);

	std::cout << "checking, please wait";

	std::chrono::milliseconds span(10);
	while(fut.wait_for(span) == std::future_status::timeout)
		std::cout << '.';

	std::cout << "-------";
	bool x = fut.get();

	std::cout << "\n700020007 " << (x ? "is" : "is not") << " prime.\n";

	return 0;
}

std::future::wait_until

template <class Clock, class Duration>
  future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

阻塞調用wait_until()的線程直到abs_time這個時間點或者callable object已在後臺調用完成(不管是前面的哪一種狀況先發生都會返回一個std::future_status):可是若是在std::async()調用了callable object的時候使用了std::launch::deferred(推遲調用callable object),wait_until()是不會強制啓動對callable object的調用的,也會返回一個std::future_status的狀態

 

std::future::shared

shared_future<T> share();

返回一個std::shared_future,實際上是返回當前std::future的shared state,返回以後當前std::future再也不可用且當前std::future的shared state變成invalid.

#include <iostream>
#include <future>
#include <chrono>
#include <thread>

int getValue()
{
	std::this_thread::sleep_for(std::chrono::seconds(5));
	return 10;
}

int main()
{
	std::future<int> result = std::async(std::launch::async, getValue); //並行運行了getValue.
	std::shared_future<int> shared_result = result.share();     //此時的getValue還在後臺運行.

	std::cout << std::boolalpha << result.valid() << std::endl;
	std::cout << shared_result.get() << std::endl;
	std::cout << shared_result.get() << std::endl;

	return 0;
}
相關文章
相關標籤/搜索