深圳汽車網(wǎng)站建設(shè)win7優(yōu)化教程
文章目錄
- 八、day8
- 1. std::async
- 2. std::future
- 2.1 wait()
- 2.2 get()
八、day8
之前說過,std::async
內(nèi)部的處理邏輯和std::thread
相似,而且std::async
和std::future
有密不可分的聯(lián)系。今天,通過對(duì)std::async
和std::future
源碼進(jìn)行解析,了解二者的處理邏輯和關(guān)系。
源碼均基于 MSVC 實(shí)現(xiàn)
參考:
- 博主戀戀風(fēng)辰的個(gè)人博客
- up主mq白cpp的個(gè)人倉庫
1. std::async
std::async
有兩種重載實(shí)現(xiàn):
template <class _Fty, class... _ArgTypes>
_NODISCARD future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) {// manages a callable object launched with supplied policyusing _Ret = _Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>;using _Ptype = typename _P_arg_type<_Ret>::type;_Promise<_Ptype> _Pr(_Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(_STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...)));return future<_Ret>(_Pr._Get_state_for_future(), _Nil());
}template <class _Fty, class... _ArgTypes>
_NODISCARD future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>> async(_Fty&& _Fnarg, _ArgTypes&&... _Args) {// manages a callable object launched with default policyreturn _STD async(launch::async | launch::deferred, _STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...);
}
第一種重載需要顯式指定啟動(dòng)策略,也就是我們之前說的std::launch::async
、std::launch::deferred
和std::launch::async | std::launch::deferred
;第二種重載在使用默認(rèn)策略時(shí)會(huì)被調(diào)用(也就是只傳遞可調(diào)用對(duì)象和參數(shù)而不傳遞啟動(dòng)策略),在內(nèi)部會(huì)調(diào)用第一種重載并傳入一個(gè)std::launch::async | std::launch::deferred
策略,并將參數(shù)全部轉(zhuǎn)發(fā)。
我們只需要著重關(guān)注第一種重載即可:
-
模板參數(shù)和函數(shù)體外部信息:
_Fty
:可調(diào)用對(duì)象的類型_ArgTypes
:可調(diào)用對(duì)象所需的參數(shù)類型_NODISCARD
:宏,用于標(biāo)記該函數(shù)的返回值不應(yīng)被忽略
-
返回類型:
future<_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>>
其實(shí)就是返回一個(gè)
std::future
對(duì)象,_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>
是std::invoke
對(duì)給定的可調(diào)用對(duì)象_Fnarg
和參數(shù)_Args...
執(zhí)行后返回的類型,其實(shí)也就是通過_Invoke_result_t
從_Fty
和_ArgTypes...
中推導(dǎo)出的返回類型。我們之前在
thread
源碼解析中說過std::invoke
內(nèi)部其實(shí)是調(diào)用_Call
函數(shù),_Call
函數(shù)負(fù)責(zé)提供參數(shù)并調(diào)用傳入的可調(diào)用對(duì)象。我們可以把
_Invoke_result_t
看作是一個(gè)對(duì)std::invoke
的結(jié)果類型的封裝,std::invoke
是一個(gè)工具,可以調(diào)用可調(diào)用對(duì)象并返回其結(jié)果。_Invoke_result_t
提供了一種方式來“推導(dǎo)”出這個(gè)結(jié)果類型。這個(gè)類型萃取工具通常長這樣(簡化版):
template <typename _Callable, typename... _Args> struct _Invoke_result_t {using type = decltype(std::invoke(std::declval<_Callable>(), std::declval<_Args>()...)); };
上述代碼通過
std::invoke
來推導(dǎo)(decltype)_Callable
(即可調(diào)用對(duì)象)在給定參數(shù)_Args...
上執(zhí)行后的返回類型。換句話說,_Invoke_result_t<_Fty, _ArgTypes...>
的type
成員類型就是可調(diào)用對(duì)象在調(diào)用后的返回類型。值得注意的是,所有類型在傳遞前都進(jìn)行了
decay
處理,也就是將cv和const修飾符去掉,默認(rèn)按值傳遞與std::thread
的行為一致。 -
形參:
future<_Ret> async(launch _Policy, _Fty&& _Fnarg, _ArgTypes&&... _Args) {}
launch _Policy
: 表示任務(wù)的執(zhí)行策略,可以是launch::async
(表示異步執(zhí)行)或launch::deferred
(表示延遲執(zhí)行),或者std::launch::async | std::launch::deferred
_Fty&& _Fnarg
: 可調(diào)用對(duì)象,通過完美轉(zhuǎn)發(fā)機(jī)制將其轉(zhuǎn)發(fā)給實(shí)際的異步任務(wù)_ArgTypes&&... _Args
: 調(diào)用該可調(diào)用對(duì)象時(shí)所需的參數(shù),同樣通過完美轉(zhuǎn)發(fā)機(jī)制進(jìn)行轉(zhuǎn)發(fā)
-
_Ret
和_Ptype
:-
_Ret
就算我們?cè)诜祷仡愋椭姓f到的_Invoke_result_t<decay_t<_Fty>, decay_t<_ArgTypes>...>
,表示可調(diào)用對(duì)象的返回類型; -
using _Ptype = typename _P_arg_type<_Ret>::type
:_Ptype
的定義在大多數(shù)情況下和_Ret
是相同的,類模板_P_arg_type
只是為了處理引用類型以及 void 的情況,參見_P_arg_type
的實(shí)現(xiàn):template <class _Fret> struct _P_arg_type { // type for functions returning Tusing type = _Fret; };template <class _Fret> struct _P_arg_type<_Fret&> { // type for functions returning reference to Tusing type = _Fret*; };template <> struct _P_arg_type<void> { // type for functions returning voidusing type = int; };
為什么需要 _Ptype ?
在異步任務(wù)的實(shí)現(xiàn)中,
std::promise
是用于將結(jié)果與std::future
綁定的對(duì)象。std::promise
的模板參數(shù)通常是可調(diào)用對(duì)象返回值的類型。在std::async
函數(shù)中,我們需要?jiǎng)?chuàng)建一個(gè)std::promise
對(duì)象來存儲(chǔ)任務(wù)的結(jié)果,因此我們需要計(jì)算出正確的承諾類型(promise type
)。也就是說,定義_Ptype
是為了配合后面_Promise
的使用,確保任務(wù)的結(jié)果可以通過std::future
獲取。-
_Ret
是任務(wù)返回的類型(由_Invoke_result_t
推導(dǎo)出)。 -
_Ptype
就是這個(gè)返回類型的承諾類型。也就是說,**_Ptype
是std::promise
的模板參數(shù)類型,**表示這個(gè)任務(wù)結(jié)果的類型。
_Ptype
的定義在大多數(shù)情況下和_Ret
是相同的,都是可調(diào)用對(duì)象返回值的類型。 -
-
-
_Promise<_Ptype> _Pr
:創(chuàng)建一個(gè)std::promise
對(duì)象_Pr
,其類型為_Ptype
,表示與異步任務(wù)的結(jié)果相關(guān)聯(lián)的承諾(promise)。_Promise
類型我們之前講過,這里就不在敘述它的作用,關(guān)鍵還在于其存儲(chǔ)的數(shù)據(jù)成員:template <class _Ty> class _Promise { public:_Promise(_Associated_state<_Ty>* _State_ptr) : _State(_State_ptr, false), _Future_retrieved(false) {}_Promise(_Promise&& _Other) : _State(_STD move(_Other._State)), _Future_retrieved(_Other._Future_retrieved) {}_Promise& operator=(_Promise&& _Other) {_State = _STD move(_Other._State);_Future_retrieved = _Other._Future_retrieved;return *this;}~_Promise() noexcept {}void _Swap(_Promise& _Other) {_State._Swap(_Other._State);_STD swap(_Future_retrieved, _Other._Future_retrieved);}const _State_manager<_Ty>& _Get_state() const {return _State;}_State_manager<_Ty>& _Get_state() {return _State;}_State_manager<_Ty>& _Get_state_for_set() {if (!_State.valid()) {_Throw_future_error(make_error_code(future_errc::no_state));}return _State;}_State_manager<_Ty>& _Get_state_for_future() {if (!_State.valid()) {_Throw_future_error(make_error_code(future_errc::no_state));}if (_Future_retrieved) {_Throw_future_error(make_error_code(future_errc::future_already_retrieved));}_Future_retrieved = true;return _State;}bool _Is_valid() const noexcept {return _State.valid();}bool _Is_ready() const {return _State._Is_ready();}bool _Is_ready_at_thread_exit() const {return _State._Is_ready_at_thread_exit();}_Promise(const _Promise&) = delete;_Promise& operator=(const _Promise&) = delete;private:_State_manager<_Ty> _State;bool _Future_retrieved; };
注意:
_Promise
和std::promise
并不是同一個(gè)模板類,_Promise
是為了提供對(duì)std::promise
的進(jìn)一步定制,并不是std::primse
本身。std::primise
模板類的私有成員是通過_Promise
聲明的,即// std::primise 的私有成員 private:_Promise<_Ty*> _MyPromise;
_Promise
類模板是對(duì)_State_manager
類模板的包裝,并增加了一個(gè)表示狀態(tài)的私有成員_Future_retrieved
。private:_State_manager<_Ty> _State;bool _Future_retrieved;
狀態(tài)成員用于跟蹤
_Promise
是否已經(jīng)調(diào)用過_Get_state_for_future()
成員函數(shù);它默認(rèn)為false
,在第一次調(diào)用_Get_state_for_future()
成員函數(shù)時(shí)被置為true
,如果二次調(diào)用,就會(huì)拋出future_errc::future_already_retrieved
異常。_Promise
的構(gòu)造函數(shù)接受的不是_State_manager
類型的對(duì)象,而是_Associated_state
類型的指針,用來初始化數(shù)據(jù)成員_State
。_Promise(_Associated_state<_Ty>* _State_ptr) : _State(_State_ptr, false), _Future_retrieved(false) {}
這是因?yàn)閷?shí)際上
_State_manager
類型只有兩個(gè)私有成員:Associated_state
指針,以及一個(gè)狀態(tài)成員:private:_Associated_state<_Ty>* _Assoc_state;bool _Get_only_once;
可以簡單理解為
_State_manager
是對(duì)Associated_state
的包裝,其中的大部分接口實(shí)際上是調(diào)用_Assoc_state
的成員函數(shù)(你們可以去_State_manager
的實(shí)現(xiàn)源碼中查閱,大部分接口其實(shí)都是通過調(diào)用_Assoc_state
實(shí)現(xiàn)的)。所以在解析
std::async
源碼之前,我們必須對(duì)Associated_state
有一個(gè)清晰的了解:public:_Ty _Result;exception_ptr _Exception;mutex _Mtx;condition_variable _Cond;bool _Retrieved;int _Ready;bool _Ready_at_thread_exit;bool _Has_stored_result;bool _Running;
這是
Associated_state
模板類主要的成員變量(我沒有全部列上去,只列了主要的),其中,最為重要的三個(gè)變量是:異常指針、互斥量、條件變量。其實(shí),
_Associated_state
模板類負(fù)責(zé)管理異步任務(wù)的狀態(tài),包括結(jié)果的存儲(chǔ)、異常的處理以及任務(wù)完成的通知。它是實(shí)現(xiàn)std::future
和std::promise
的核心組件之一,通過_State_manager
和_Promise
類模板對(duì)其進(jìn)行封裝和管理,提供更高級(jí)別的接口和功能。_Promise
、_State_manager
、_Associated_state
之間的包含關(guān)系如上述結(jié)構(gòu)所示。 -
初始化
_Promise
對(duì)象:_Promise<_Ptype> _Pr(_Get_associated_state<_Ret>(_Policy, _Fake_no_copy_callable_adapter<_Fty, _ArgTypes...>(_STD forward<_Fty>(_Fnarg), _STD forward<_ArgTypes>(_Args)...)) );
這是一個(gè)函數(shù)調(diào)用,將我們
std::async
的參數(shù)全部轉(zhuǎn)發(fā)給它。- 首先將參數(shù)
_Fnarg
(可調(diào)用對(duì)象)和_Args...
(傳入可調(diào)用對(duì)象的參數(shù)包) 通過std::forward
轉(zhuǎn)發(fā)給_Fake_no_copy_callable_adapter
。 - 然后,
_Fake_no_copy_callable_adapter
創(chuàng)建一個(gè)可調(diào)用對(duì)象(函數(shù)適配器)。 - 接著,適配器和指定的啟動(dòng)策略被傳遞給
_Get_associated_state
函數(shù),目的是獲取與異步操作相關(guān)的狀態(tài)。 - 最終,
_Get_associated_state
返回一個(gè)與異步操作相關(guān)的狀態(tài),并將其傳遞給_Pr
,這將會(huì)返回一個(gè)_Promise<_Ptype>
,代表一個(gè)異步操作的結(jié)果。
_Get_associated_state
函數(shù)根據(jù)啟動(dòng)模式(_Policy,有三種)來決定創(chuàng)建的異步任務(wù)狀態(tài)對(duì)象類型:template <class _Ret, class _Fty> _Associated_state<typename _P_arg_type<_Ret>::type>* _Get_associated_state(launch _Psync, _Fty&& _Fnarg) {// construct associated asynchronous state object for the launch typeswitch (_Psync) { // select launch typecase launch::deferred:return new _Deferred_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));case launch::async: // TRANSITION, fixed in vMajorNext, should create a new thread heredefault:return new _Task_async_state<_Ret>(_STD forward<_Fty>(_Fnarg));} }
_Get_associated_state
函數(shù)返回一個(gè)_Associated_state
指針(_Associated_state
可用于初始化_State_manager
,_State_manager
可用于初始化_Promise
),該指針指向一個(gè)新的_Deferred_async_state
或_Task_async_state
對(duì)象。這兩個(gè)類分別對(duì)應(yīng)于異步任務(wù)的兩種不同執(zhí)行策略:延遲執(zhí)行和異步執(zhí)行。這段代碼也很好的說明,
launch::async | launch::deferred
和launch::async
的行為是相同的,都會(huì)創(chuàng)建新線程異步執(zhí)行任務(wù),只不過前者會(huì)自行判斷系統(tǒng)資源來抉擇。
_Task_async_state
與_Deferred_async_state
都繼承自_Packaged_state
,其用于異步執(zhí)行任務(wù)。它們的構(gòu)造函數(shù)都接受一個(gè)函數(shù)對(duì)象,并將其轉(zhuǎn)發(fā)給基類_Packaged_state
的構(gòu)造函數(shù)。// _Task_async_state 的構(gòu)造函數(shù) template <class _Rx> class _Task_async_state : public _Packaged_state<_Rx()> // _Deferred_async_state 的構(gòu)造函數(shù) template <class _Rx> class _Deferred_async_state : public _Packaged_state<_Rx()>
_Packaged_state
類型只有一個(gè)數(shù)據(jù)成員 :std::function
類型的對(duì)象_Fn
,它用來存儲(chǔ)需要執(zhí)行的異步任務(wù),而它又繼承自_Associated_state
。template <class _Ret, class... _ArgTypes> class _Packaged_state<_Ret(_ArgTypes...)>: public _Associated_state<_Ret>
如上圖所示,
_Task_async_state
與_Deferred_async_state
都繼承自_Packaged_state
,_Packaged_state
中保存了傳入給std::async
的可調(diào)用對(duì)象。同時(shí),_Packaged_state
繼承自_Associated_state
,_Associated_state
是_Primise
類中成員_State
的最基本組成對(duì)象,基本所有的接口都是通過調(diào)用_Associated_state
的函數(shù)實(shí)現(xiàn)的。_Task_async_state
與_Deferred_async_state
的構(gòu)造函數(shù)如下:// _Task_async_state template <class _Fty2> _Task_async_state(_Fty2&& _Fnarg) : _Mybase(_STD forward<_Fty2>(_Fnarg)) {_Task = ::Concurrency::create_task([this]() { // do it nowthis->_Call_immediate();});this->_Running = true; } // _Deferred_async_state template <class _Fty2> _Deferred_async_state(const _Fty2& _Fnarg) : _Packaged_state<_Rx()>(_Fnarg) {} template <class _Fty2> _Deferred_async_state(_Fty2&& _Fnarg) : _Packaged_state<_Rx()>(_STD forward<_Fty2>(_Fnarg)) {}
a. _Task_async_state
_Task_async_state
有一個(gè)數(shù)據(jù)成員_Task
用于從線程池中獲取線程,并執(zhí)行可調(diào)用對(duì)象:private:::Concurrency::task<void> _Task;
_Task_async_state
的實(shí)現(xiàn)使用了微軟實(shí)現(xiàn)的并行模式庫(PPL)。簡而言之,launch::async
策略并不是單純的創(chuàng)建線程讓任務(wù)執(zhí)行,而是使用了微軟的::Concurrency::create_task
,它從線程池中獲取線程并執(zhí)行任務(wù)返回包裝對(duì)象。this->_Call_immediate()
是調(diào)用_Task_async_state
的父類_Packaged_state
的成員函數(shù)_Call_immediate
._Packaged_state
有三個(gè)版本,自然_Call_immediate
也有三種版本,用于處理可調(diào)用對(duì)象返回類型的三種情況:// 返回普通類型 // class _Packaged_state<void(_ArgTypes...)> void _Call_immediate(_ArgTypes... _Args) {_TRY_BEGIN// 調(diào)用函數(shù)對(duì)象并捕獲異常 傳遞返回值this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false);_CATCH_ALL// 函數(shù)對(duì)象拋出異常就記錄this->_Set_exception(_STD current_exception(), false);_CATCH_END }// 返回引用類型 // class _Packaged_state<_Ret&(_ArgTypes...)> void _Call_immediate(_ArgTypes... _Args) {_TRY_BEGIN// 調(diào)用函數(shù)對(duì)象并捕獲異常 傳遞返回值的地址this->_Set_value(_STD addressof(_Fn(_STD forward<_ArgTypes>(_Args)...)), false);_CATCH_ALL// 函數(shù)對(duì)象拋出異常就記錄this->_Set_exception(_STD current_exception(), false);_CATCH_END }// 返回void類型 // class _Packaged_state<void(_ArgTypes...)> void _Call_immediate(_ArgTypes... _Args) { _TRY_BEGIN// 調(diào)用函數(shù)對(duì)象并捕獲異常 因?yàn)榉祷?void 不獲取返回值 而是直接 _Set_value 傳遞一個(gè) 1_Fn(_STD forward<_ArgTypes>(_Args)...);this->_Set_value(1, false);_CATCH_ALL// 函數(shù)對(duì)象拋出異常就記錄this->_Set_exception(_STD current_exception(), false);_CATCH_END }
_Fn(_STD forward<_ArgTypes>(_Args)...
表示執(zhí)行可調(diào)用對(duì)象,this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...)
表示將可調(diào)用對(duì)象的返回值傳入給_Set_value
,其他兩個(gè)函數(shù)也是類似的處理過程。_TRY_BEGIN
、_CATCH_ALL
、_CATCH_END
類似try-catch塊
。當(dāng)_Fn
函數(shù)對(duì)象拋出異常時(shí),控制流會(huì)跳轉(zhuǎn)到_CATCH_ALL
代碼塊;this->_Set_exception
用來記錄當(dāng)前捕獲的異常;_CATCH_END
標(biāo)識(shí)異常處理的結(jié)束;因?yàn)榉祷仡愋蜑?code>void 表示不獲取返回值,所以這里通過_Set_value
傳遞一個(gè) 1(表示正確執(zhí)行的狀態(tài))。所有的返回值均傳入給_Set_value
。簡而言之,就是把返回引用類型的可調(diào)用對(duì)象返回值的引用獲取地址傳遞給
_Set_value
,把返回 void 類型的可調(diào)用對(duì)象傳遞一個(gè) 1 (表示正確執(zhí)行的狀態(tài))給_Set_value
。_Set_value
、_set_exception
函數(shù)來自_Packaged_state
模板類的父類_Associated_state
,通過這兩個(gè)函數(shù),傳遞的可調(diào)用對(duì)象執(zhí)行結(jié)果,以及可能的異常,并將結(jié)果或異常存儲(chǔ)在_Associated_state
中。b. _Deferred_async_state
_Deferred_async_state
并不會(huì)從線程池中獲取一個(gè)新線程,然后再新線程中執(zhí)行任務(wù),而是當(dāng)前線程調(diào)用future的get或者wait函數(shù)時(shí),在當(dāng)前線程中同步執(zhí)行。但它同樣調(diào)用_Call_immediate
函數(shù)執(zhí)行存儲(chǔ)的可調(diào)用對(duì)象,它有一個(gè)_Run_deferred_function
函數(shù):void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function_Lock.unlock();_Packaged_state<_Rx()>::_Call_immediate();_Lock.lock(); }
然后通過
_Call_immediate
調(diào)用可調(diào)用對(duì)象并通過函數(shù)_Set_value
、_set_exception
存儲(chǔ)可調(diào)用對(duì)象返回結(jié)果或者異常至_Associated_state
。 - 首先將參數(shù)
-
返回
std::future
return future<_Ret>(_Pr._Get_state_for_future(), _Nil());
_Ret
在前面說了,其實(shí)就是可調(diào)用對(duì)象返回值的類型。傳給
future
構(gòu)造函數(shù)的參數(shù)之一是:_Pr._Get_state_for_future()
,調(diào)用上面構(gòu)造的_Promise
的成員函數(shù)_Get_state_for_future
,該函數(shù)用于返回_Promise
類的私有成員變量_State
。_Get_state_for_future
函數(shù)的實(shí)現(xiàn)如下:_State_manager<_Ty>& _Get_state_for_future() {if (!_State.valid()) {_Throw_future_error2(future_errc::no_state);}if (_Future_retrieved) {_Throw_future_error2(future_errc::future_already_retrieved);}_Future_retrieved = true;return _State; }
其實(shí)就是調(diào)用
_State
的成員函數(shù)valid()
檢查狀態(tài)(是否有錯(cuò)),然后判斷future
是否提前返回可調(diào)用對(duì)象的返回值(如果是,代表future的get被調(diào)用,拋出異常);最后,返回_State
。
2. std::future
我們首先從一個(gè)最簡單的std::async
示例開始:
std::future<int> future = std::async([] { return 0; });
future.get();
我們從之前的學(xué)習(xí)中了解到,future.get()
就是從future
中獲取可調(diào)用對(duì)象的返回結(jié)果。唯一的問題是:future.get()
內(nèi)部執(zhí)行了什么流程?首先從future
的實(shí)現(xiàn)開始:
_EXPORT_STD template <class _Ty>
class future : public _State_manager<_Ty> {// class that defines a non-copyable asynchronous return object that holds a value
private:using _Mybase = _State_manager<_Ty>;public:static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>,"T in future<T> must meet the Cpp17Destructible requirements (N4950 [futures.unique.future]/4).");future() = default;future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {}future& operator=(future&&) = default;future(_From_raw_state_tag, const _Mybase& _State) noexcept : _Mybase(_State, true) {}_Ty get() {// block until ready then return the stored result or throw the stored exceptionfuture _Local{_STD move(*this)};return _STD move(_Local._Get_value());}_NODISCARD shared_future<_Ty> share() noexcept {return shared_future<_Ty>(_STD move(*this));}future(const future&) = delete;future& operator=(const future&) = delete;
};
future
類繼承自_State_manager
類,_State_manager
類又有一個(gè)_Associated_state<_Ty>*
類型的私有成員_State
,而_State_manager
的接口實(shí)現(xiàn)大部分是通過調(diào)用_Associated_state
的成員函數(shù)實(shí)現(xiàn)的。關(guān)系如下:
2.1 wait()
但你可能發(fā)現(xiàn)一個(gè)問題,
future
類怎么沒有wait()
成員函數(shù)????其實(shí),wait()
函數(shù)繼承自父類_State_manager
。
void wait() const { // wait for signalif (!valid()) {_Throw_future_error2(future_errc::no_state);}_Assoc_state->_Wait();
}
而_State_manager
類的wait()
其實(shí)是通過調(diào)用_Associated_state
的接口實(shí)現(xiàn)的,所以說,_Associated_state
在std::async
和std::future
中是非常核心的。
virtual void _Wait() { // wait for signalunique_lock<mutex> _Lock(_Mtx);_Maybe_run_deferred_function(_Lock);while (!_Ready) {_Cond.wait(_Lock);}
}
_Associated_state
的wait()
函數(shù)通過unique_lock
保護(hù)共享數(shù)據(jù),然后調(diào)用_Maybe_run_deferred_function
執(zhí)行可調(diào)用對(duì)象,直至調(diào)用結(jié)束。
void _Maybe_run_deferred_function(unique_lock<mutex>& _Lock) { // run a deferred function if not already doneif (!_Running) { // run the function_Running = true;_Run_deferred_function(_Lock);}
}
_Maybe_run_deferred_function
其實(shí)就是通過調(diào)用_Run_deferred_function
來調(diào)用_Call_immediate()
,我們?cè)?code>async源碼中學(xué)習(xí)過_Run_deferred_function
和`_Call_immediate()。
void _Run_deferred_function(unique_lock<mutex>& _Lock) override { // run the deferred function_Lock.unlock();_Packaged_state<_Rx()>::_Call_immediate();_Lock.lock();
}
在 _Wait
函數(shù)中調(diào)用 _Maybe_run_deferred_function
是為了確保延遲執(zhí)行(launch::deferred
)的任務(wù)能夠在等待前被啟動(dòng)并執(zhí)行完畢。這樣,在調(diào)用 wait
時(shí)可以正確地等待任務(wù)完成。
因?yàn)橹挥?code>std::launch::deferred才是當(dāng)調(diào)用future.get或者wait
時(shí)才會(huì)執(zhí)行_Call_immediate()
,其他兩種啟動(dòng)策略在大部分情況下都是直接執(zhí)行,通過future.get
獲得結(jié)果。所以我們必須保證在調(diào)用wait
函數(shù)時(shí),執(zhí)行std::launch::deferred
策略的任務(wù)被執(zhí)行,而其他兩種啟動(dòng)策略早已經(jīng)執(zhí)行任務(wù),無需再調(diào)用_Call_immediate()
。所以在_Maybe_run_deferred_function
函數(shù)中,有下面一段,判斷任務(wù)是否以及執(zhí)行,如果被執(zhí)行,那么久就不調(diào)用_Call_immediate
,反之調(diào)用。
if (!_Running) { // run the function_Running = true;_Run_deferred_function(_Lock);
while (!_Ready) {_Cond.wait(_Lock);}
通過條件變量掛起當(dāng)前線程,等待可調(diào)用對(duì)象執(zhí)行完畢。在等待期間,當(dāng)前線程釋放持有的鎖,保證其他線程再次期間可以訪問到共享資源,待當(dāng)前線程被喚醒后,重新持有鎖。其主要作用是:
- 避免虛假喚醒:
- 條件變量的
wait
函數(shù)在被喚醒后,會(huì)重新檢查條件(即_Ready
是否為true
),確保只有在條件滿足時(shí)才會(huì)繼續(xù)執(zhí)行。這防止了由于虛假喚醒導(dǎo)致的錯(cuò)誤行為。
- 條件變量的
- 等待
launch::async
的任務(wù)在其它線程執(zhí)行完畢:- 對(duì)于
launch::async
模式的任務(wù),這段代碼確保當(dāng)前線程會(huì)等待任務(wù)在另一個(gè)線程中執(zhí)行完畢,并接收到任務(wù)完成的信號(hào)。只有當(dāng)任務(wù)完成并設(shè)置_Ready
為true
后,條件變量才會(huì)被通知,從而結(jié)束等待。
- 對(duì)于
這樣,當(dāng)調(diào)用 wait
函數(shù)時(shí),可以保證無論任務(wù)是 launch::deferred
還是 launch::async
模式,當(dāng)前線程都會(huì)正確地等待任務(wù)的完成信號(hào),然后繼續(xù)執(zhí)行。
std::future
其實(shí)還有兩種特化,不過整體大差不差。
template <class _Ty>
class future<_Ty&> : public _State_manager<_Ty*>
template <>
class future<void> : public _State_manager<int>
也就是對(duì)返回類型為引用和 void 的情況了。其實(shí)先前已經(jīng)聊過很多次了,無非就是內(nèi)部的返回引用實(shí)際按指針操作,返回 void,那么也得給個(gè) 1,表示正常運(yùn)行的狀態(tài)。類似于前面 _Call_immediate
的實(shí)現(xiàn)。
2.2 get()
get()
函數(shù)是future
的成員函數(shù),而沒有繼承父類_State_manager
:
// std::future<void>
void get() {// block until ready then return or throw the stored exceptionfuture _Local{_STD move(*this)};_Local._Get_value();
}
// std::future<T>
_Ty get() {// block until ready then return the stored result or throw the stored exceptionfuture _Local{_STD move(*this)};return _STD move(_Local._Get_value());
}
// std::future<T&>
_Ty& get() {// block until ready then return the stored result or throw the stored exceptionfuture _Local{_STD move(*this)};return *_Local._Get_value();
}
因?yàn)?code>future有三種特化,所以get()
函數(shù)也有三種特化。它們將當(dāng)前future
對(duì)象的指針通過std::move
轉(zhuǎn)移給類型為future
的局部變量_Local
(轉(zhuǎn)移后,原本的future
對(duì)象便失去了所有權(quán))。然后,局部變量_Local
調(diào)用成員函數(shù)_Get_value()
,并將結(jié)果返回。
注意:局部對(duì)象
_Local
在函數(shù)結(jié)束時(shí)析構(gòu)。這意味著當(dāng)前對(duì)象(*this
)失去共享狀態(tài),并且狀態(tài)被完全銷毀。
_Get_value()
函數(shù)的實(shí)現(xiàn)如下:
_Ty& _Get_value() const {if (!valid()) {_Throw_future_error2(future_errc::no_state);}return _Assoc_state->_Get_value(_Get_only_once);
}
future.valid()
成員函數(shù)檢查 future 當(dāng)前是否關(guān)聯(lián)共享狀態(tài),即是否當(dāng)前關(guān)聯(lián)任務(wù)。如果還未關(guān)聯(lián),或者任務(wù)已經(jīng)執(zhí)行完(調(diào)用了 get()、set()),都會(huì)返回 false
。
- 首先,通過
valid()
判斷當(dāng)前future
對(duì)象是否關(guān)聯(lián)共享狀態(tài),如果沒,拋出異常。 - 最后,調(diào)用
_Assoc_state
的成員函數(shù)_Get_value
,傳遞_Get_only_once
參數(shù),其實(shí)就是代表這個(gè)成員函數(shù)只能調(diào)用一次。
_Assoc_state
的類型是 _Associated_state<_Ty>*
,是一個(gè)指針類型,它實(shí)際會(huì)指向自己的子類對(duì)象,我們?cè)谥v std::async
源碼的時(shí)候提到了,它必然指向 _Deferred_async_state
或者 _Task_async_state
。
_Assoc_state->_Get_value
這其實(shí)是個(gè)多態(tài)調(diào)用,父類有這個(gè)虛函數(shù):
virtual _Ty& _Get_value(bool _Get_only_once) {unique_lock<mutex> _Lock(_Mtx);if (_Get_only_once && _Retrieved) {_Throw_future_error2(future_errc::future_already_retrieved);}if (_Exception) {_STD rethrow_exception(_Exception);}// TRANSITION: `_Retrieved` should be assigned before `_Exception` is thrown so that a `future::get`// that throws a stored exception invalidates the future (N4950 [futures.unique.future]/17)_Retrieved = true;_Maybe_run_deferred_function(_Lock);while (!_Ready) {_Cond.wait(_Lock);}if (_Exception) {_STD rethrow_exception(_Exception);}if constexpr (is_default_constructible_v<_Ty>) {return _Result;} else {return _Result._Held_value;}
}
子類 _Task_async_state
對(duì)其進(jìn)行了重寫,以 launch::async
策略或者std::launch::async | std::launch::deferred
策略創(chuàng)建的future
,實(shí)際會(huì)調(diào)用 _Task_async_state::_Get_value
:
_State_type& _Get_value(bool _Get_only_once) override {// return the stored result or throw stored exception_Task.wait();return _Mybase::_Get_value(_Get_only_once);
}
_Deferred_async_state
沒有對(duì)其進(jìn)行重寫,直接調(diào)用父類虛函數(shù)。
_Task
就是 ::Concurrency::task<void> _Task;
,調(diào)用 wait()
成員函數(shù)確保任務(wù)執(zhí)行完畢。
_Mybase::_Get_value(_Get_only_once)
其實(shí)又是回去調(diào)用父類的虛函數(shù)了。
_Get_value
方法詳解
-
_Get_value()只能調(diào)用一次
如果
_Get_only_once
為true
且_Retrieved
為true
(表示結(jié)果已經(jīng)被檢索過),則拋出future_already_retrieved
錯(cuò)誤。 -
處理異常
如果在獲取結(jié)果過程中出現(xiàn)了異常,需要重新拋出該異常
-
設(shè)置
_Retrieved
為true
在獲取值之前,設(shè)置
_Retrieved
為true
,表示結(jié)果已經(jīng)被檢索過。這樣可以確保future
對(duì)象不會(huì)被重復(fù)獲取,避免多次調(diào)用get
時(shí)引發(fā)錯(cuò)誤。 -
執(zhí)行延遲函數(shù)
調(diào)用
_Maybe_run_deferred_function
來運(yùn)行可能的延遲任務(wù)。在該函數(shù)內(nèi)部,如果任務(wù)已經(jīng)運(yùn)行,那么退出,如果沒運(yùn)行,調(diào)用_Call_immediate()
函數(shù)執(zhí)行可調(diào)用對(duì)象。 -
等待結(jié)果
使用條件變量掛起當(dāng)前線程,確保線程同步,即只有當(dāng)異步任務(wù)準(zhǔn)備好返回結(jié)果時(shí),線程才會(huì)繼續(xù)執(zhí)行。
-
再次檢查異常
線程被喚醒將結(jié)果存儲(chǔ)至
future
對(duì)象中后,再次判斷是否發(fā)生了異常,需要重新拋出異常 -
返回結(jié)果
這部分代碼根據(jù)
_Ty
類型的特性決定如何返回結(jié)果:- 如果
_Ty
類型是 默認(rèn)可構(gòu)造(即_Ty
的默認(rèn)構(gòu)造函數(shù)有效),直接返回_Result
。 - 否則,返回
_Result._Held_value
is_default_constructible_v<_Ty>
是一個(gè)C++17
引入的類型特征,用于檢查類型_Ty
是否具有默認(rèn)構(gòu)造函數(shù)。_Result
是future
中持有的結(jié)果,而_Held_value
是存儲(chǔ)在_Result
中的實(shí)際值。 - 如果
_Result
是通過執(zhí)行 _Call_immediate
函數(shù),然后 _Call_immediate
再執(zhí)行 _Set_value
,_Set_value
再執(zhí)行 _Set_value_raw
,_Set_value_raw
再執(zhí)行_Emplace_result
并通知線程可以醒來,_Emplace_result
獲取到我們執(zhí)行任務(wù)的返回值的。以 Ty
的偏特化為例:
// _Packaged_state
void _Call_immediate(_ArgTypes... _Args) {_TRY_BEGIN// 調(diào)用函數(shù)對(duì)象并捕獲異常 傳遞返回值this->_Set_value(_Fn(_STD forward<_ArgTypes>(_Args)...), false);_CATCH_ALL// 函數(shù)對(duì)象拋出異常就記錄this->_Set_exception(_STD current_exception(), false);_CATCH_END
}// _Asscoiated_state
void _Set_value(const _Ty& _Val, bool _At_thread_exit) { // store a resultunique_lock<mutex> _Lock(_Mtx);_Set_value_raw(_Val, &_Lock, _At_thread_exit);
}
void _Set_value_raw(const _Ty& _Val, unique_lock<mutex>* _Lock, bool _At_thread_exit) {// store a result while inside a locked blockif (_Already_has_stored_result()) {_Throw_future_error2(future_errc::promise_already_satisfied);}_Emplace_result(_Val);_Do_notify(_Lock, _At_thread_exit);
}
template <class _Ty2>
void _Emplace_result(_Ty2&& _Val) {// TRANSITION, incorrectly assigns _Result when _Ty is default constructibleif constexpr (is_default_constructible_v<_Ty>) {_Result = _STD forward<_Ty2>(_Val); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!} else {::new (static_cast<void*>(_STD addressof(_Result._Held_value))) _Ty(_STD forward<_Ty2>(_Val));_Has_stored_result = true;}
}