對於初學者而言,「以多線程運行程序」的最佳起點就是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; }