簡介
C++11之前,主線程要想獲取子線程的返回值,一般都是通過全局變量,或者類似機制。C++11開始為我們提供了一組方法來獲取子線程的返回值,并保證其原子性。
頭文件
#include <future>
std::promise
在promise中保存了一個值或者異常,可以通過與其關聯的 std::future 來獲取這個值或者異常。常規用法
promise有兩個特化的版本
template< class R > class promise; // 普通類模板
template< class R > class promise<R&>; // 引用特化
template<> class promise<void>; // void特化
promise提供的方法
構造方法
promise(); // 默認構造方法
template< class Alloc >
promise( std::allocator_arg_t, const Alloc& alloc ); // cppreference上說是基于一個共享狀態構造promise,還沒有深入研究具體怎么用
promise( promise&& other ) noexcept; // 移動構造方法
promise( const promise& other ) = delete; // 刪除了拷貝構造方法
析構方法
析構的時機有兩個
- promise內部共享狀態為ready時
- 如果promise的狀態沒有被設置為ready析構時,會將std::future_error異常保存到共享狀態里
和構造方法類似,promise只支持移動賦值操作符
promise& operator=( promise&& other ) noexcept;
promise& operator=( const promise& rhs ) = delete;
獲取future,如果get_future是被重復調用的,那么就會拋出std::future_error異常。如果想多個地方獲取future,需要使用std::shared_future,他可以通過std::future構造。這是std::shared_future相應的構造方法的聲明:shared_future( std::future&& other )
std::future<R> get_future();
設置共享數據,以及將共享狀態設置為ready。由于promise有特化版本,下面聲明的方法有些版本沒有。另外,如果重復調用set_value會拋出std::future_error異常,即set_value只能調用一次。
void set_value( const R& value ); // member only of generic promise template
void set_value( R&& value ); //member only of generic promise template
void set_value( R& value ); // member only of promise<R&> template specialization
void set_value(); // 設置共享狀態為ready
設置異常,以及將共享狀態設置為ready,例子
void set_exception( std::exception_ptr p );
set_value_at_thread_exit 和 set_exception_at_thread_exit 類似set_value 和 set_exception,不同點是 xx_at_thread_exit 是在線程結束的時候將共享狀態設置為ready,而set_xx 是立刻設置為ready。
std::future
future類是獲取異步操作返回的封裝。promise相當于是set,future相當于是get
類似promise,future也有兩個特化版本
template< class T > class future;
template< class T > class future<T&>;
template<> class future<void>;
future提供的方法
構造方法
future() noexcept; // 不關聯任何共享狀態
future( future&& other ) // 移動構造
future( const future& other ) = delete; // 拷貝構造被刪除
只有移動賦值操作符
future& operator=( future&& other ) noexcept;
future& operator=( const future& other ) = delete;
從構造方法和移動賦值操作符可以看出,future是唯一關聯共享狀態的,如果想多個future關聯一個共享狀態,future是不行的,需要將其轉為std::shared_future。對應的方法是share:
std::shared_future<T> share() noexcept;
share方法內部是這么構造shared_future的:std::shared_future(std::move(*this)),所以調用share方法后,原理的future就不在關聯共享狀態了,也就不能再調用相關get方法,否則會拋出異常
std::shared_future
和std::future唯一的不同就是,std::shared_future可以多個對象同時關聯同一個共享狀態。他的特化版本、提供的方法和std::future也基本類似:
std::packaged_task
將可調用對象(函數,lambda表達式,仿函數等)包裝后,可以異步調用,通過std::future獲取返回值。參考代碼
std::packaged_task 提供的方法
構造方法
packaged_task() noexcept;template< class F >
explicit packaged_task( F&& f ); // f為可調用對象,他是一個萬能引用,即構造package_task的時候,可以傳遞給他可調用對象的左值,右值,左值引用,右值引用;參考:https://zhuanlan.zhihu.com/p/99524127template< class F, class Allocator >
explicit packaged_task( std::allocator_arg_t, const Allocator& a, F&& f ); // C++17刪除了該方法packaged_task( const packaged_task& ) = delete;packaged_task( packaged_task&& rhs ) noexcept; // 移動構造方法
析構方法
如果在共享狀態設置為ready之前析構package_task,會拋出 std::future_errc::broken_promise 異常
只有移動賦值操作符
packaged_task& operator=( const packaged_task& ) = delete;
packaged_task& operator=( packaged_task&& rhs ) noexcept;
判斷package_task對象是否有共享狀態,比如用無參的構造方法構造的對象,就沒有共享狀態
bool valid() const noexcept;
獲取future,如果重復調用get_future,會拋出std::future_error異常;另外,如果valid()方法返回為false,調用get_future,也會拋出std::future_error異常
std::future<R> get_future();
執行保存的可調用對象,和get_future一樣重復調用,或者pakage_task對象沒有共享狀態,都會拋出std::future_error異常
void operator()( ArgTypes... args );
在線程結束,局部變量釋放后,將共享狀態設置為ready。拋出異常的情況和operator()一樣
void make_ready_at_thread_exit( ArgTypes... args );
重置package_task的共享狀態,等價于 *this = packaged_task(std::move(f)), f 是可調用對象。此時會構造新的共享狀態,如果沒有足夠內存,會拋出std::bad_alloc異常。如果package_task對象沒有共享狀態,就會拋出std::future_error異常
void reset();
std::async
異步直行一個可調用對象
enum class launch : /* unspecified */ {async = /* unspecified */, // 立刻異步直行deferred = /* unspecified */, // /* implementation-defined */
};
當使用launch::deferred時,只有future調用get或者wait方法時,才會異步直行可調用對象