標籤:c++ 並發 async future
C++標準庫使用期望(future)來支援一次性事件的等待。要等待某種一次性事件的線程可以擷取一個代表該該事件的期望。這個線程可以每隔一段事件周期性的查詢這個期望。此外,這個線程也可以繼續做其它的處理,直到需要等待這個一次性事件才被掛起。通到期望還可以可以傳遞資料。
C++標準庫提供了兩種期望unique future(std::future<>)和shared futures(std::shared_future<>),都聲明在<future>庫標頭檔中。std::future執行個體只能關聯到一個事件。而多個std::shared_future可以關聯到同一個事件。共用期望所關聯的事件發生時,所有期望執行個體都將同時被喚醒,而去訪問這個事件。由於期望可以關聯資料,所以期望都是模版類,沒有資料關聯到期望時,就可以使用std::future<void>和std::shared_future<void>。雖然期望被用於線程間通訊,但是期望對象並不支援同步的訪問,我們需要使用mutex之類的機制,來保護不同線程對它們的訪問。
假設我們有一個需要較長時間的計算操作,我們要使用它的計算結果,在需要這個計算結果之前,我們可以做一些其它的操作。我們可以啟動一個新的線程來做計算。std:;thread沒有直接的機制來滿足我們的需求。這裡可以使用std::async函數模版來實現這個功能。
std::async可以啟動一個非同步線程,我們無需像使用thread一樣,立即開始等待這個非同步線程執行結束,std::async會返回一個期望對象,這個對象最終會用來儲存非同步線程的返回結果。當我們需要這個結果時,對期望對象調用wait就可以讓線程阻塞,直到期望準備好。例子如下:
#include <future>#include <iostream>int find_the_answer_to_ltuae(){ return 42;}void do_other_stuff(){}int main(){ std::future<int> the_answer=std::async(find_the_answer_to_ltuae); do_other_stuff(); std::cout<<"The answer is "<<the_answer.get()<<std::endl;}
std::async允許我們傳遞額外的參數給函數。如果第一個參數是一個成員函數指標,第二個參數就是成員函數對應的對象的指標,其它的參數就是傳遞給成員函數的參數。否則,第二個及後續的參數就是傳遞給作為第一個參數的函數或callable對象的參數。
#include <string>#include <future>struct X{ void foo(int,std::string const&); std::string bar(std::string const&);};X x;// 調用x->foo(42, "hello");auto f1=std::async(&X::foo,&x,42,"hello");// 調用tmpx.bar("goodbye"); tmpx是x的拷貝auto f2=std::async(&X::bar,x,"goodbye");struct Y{ double operator()(double);};Y y;// 調用tmpy(3.141);tmpy是從Y()移動構造而來auto f3=std::async(Y(),3.141);// 調用y(2.718);auto f4=std::async(std::ref(y),2.718);X baz(X&);// 調用baz(x);auto f6=std::async(baz,std::ref(x));class move_only{public: move_only(); move_only(move_only&&); move_only(move_only const&) = delete; move_only& operator=(move_only&&); move_only& operator=(move_only const&) = delete; void operator()();};// 調用tmp();tmp是通過std::move(move_only)構造而來auto f5=std::async(move_only());
預設情況下,我們需要自己決定std::async是啟動一個新的線程,還是在等待期望時同步的執行。我們可以通過傳遞給std::async的參數來控制它。參數類型為std::launch:
- std::launch::deferred表示函數調用被延遲到期望的wait()或get()被調用時。
- std::launch::async表示啟動新的線程來執行函數。
- std::launch::deferred | std::launch::async則表示通過具體實現來控制這個行為。
std::async模式使用第三種行為。
// 啟動新的線程auto f6=std::async(std::launch::async, Y(), 1.2);// 在wait()或get()被調用時執行auto f7=std::async(std::launch::deferred, baz, std::ref(x));// 取決於實現auto f8=std::async(std::launch::deferred | std::launch::async, baz, std::ref(x));auto f9=std::async(baz, std::ref(x));// 調用被延遲的函數f7.wait();
這不是讓一個std::future與一個任務執行個體相關聯的唯一方式;你也可以將任務封裝入一個std::packaged_task<>執行個體中,或使用std::promise<>類型模板。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
[C++11 並發編程] 13 使用期望等待一次性事件