文章目錄
- 描述
- 函數成員及使用
- 總結
我們上一篇描述關于C++多線程中的異步操作相關庫(
async
和
promise
),本節将分享c++标準庫中最後一個多線程異步操作庫
package_task
的學習筆記。
描述
- 頭檔案
<future>
- 聲明方式:
template< class R, class ...Args > class packaged_task<R(Args...)>;
-
簡介
标準類模版包裝了任何可調用的目标,其中包括函數,package_task
表達式,std::bind
表達式或者其他函數對象。并支援lamda
對象調用者的異步調用。調用之後的傳回值或者産生的異常能夠被存儲在能夠被package_task
std::future
對象通路的共享狀态中。
綜上描述,我們很明顯能夠體會到該模版類提供的功能和
類非常接近,但是promise
類沒有辦法初始化所有可調用的對象,promise類僅提供共享狀态的多種通路機制并提供線程之間變量的共享機制。promise
函數成員及使用
-
構造函數
構造無任務且無共享狀态的packaged_task() noexcept;
package_task
對象
template <class F> explicit packaged_task( F&& f )
構造擁有共享狀态和任務副本的 std::packaged_task 對象,
packaged_task( const packaged_task& ) = delete;
複制構造函數被删除, std::packaged_task 僅可移動
packaged_task( packaged_task&& rhs ) noexcept;
rhs 之前所占有的共享狀态和任務構造 std::packaged_task ,令 rhs 留在無共享狀态且擁有被移動後的任務的狀态
檢視如下代碼:
#include <future>
#include <iostream>
#include <thread>
int fib(int n)
{
if (n < 3) return 1;
else
{
std::cout << "fib result " << (n-1)+(n-2) << std::endl;
return (n-1) + (n-2);
}
}
int main()
{
std::packaged_task<int(int)> fib_task(&fib);
std::cout << "starting task\n";
//此時已經将package_task調用對象的執行傳回值轉交給future對象,是以後續
//的線程執行由惰性指派來觸發。即當future的對象的共享狀态嘗試擷取線程執行
//結果的時候才進行線程執行,并傳回結果。類似std::async的policy:std::launch::deferred
auto result = fib_task.get_future();
std::thread t(std::move(fib_task), 40);
std::cout << "waiting for task to finish...\n";
std::cout << result.get() << '\n';
std::cout << "task complete\n";
t.join();
}
輸出如下:
starting task
waiting for task to finish...
fib result 77
77
task complete
- 析構函數
抛棄共享狀态并銷毀存儲的任務對象,同 std::promise::~promise ,若在令共享狀态就緒前抛棄它,則存儲以 std::future_errc::broken_promise 為 error_code 的 std::future_error 異常~packaged_task()
-
指派運算符
複制指派運算符被删除, std::packaged_task 僅可移動packaged_task& operator=( const packaged_task& ) = delete
-
std::packaged_task<R(Args...)>::get_future
傳回與 *this 共享同一共享狀态的 future
同
promise
類一樣,get_future 隻能對每個 packaged_task 調用一次
成員出現異常的情況如下:get_future
- 已認證調用 get_future 取得共享狀态。設定 error_category 為 future_already_retrieved
- *this 無共享狀态。設定 error_category 為 no_state
-
std::packaged_task<R(Args...)>::make_ready_at_thread_exit
成員函數
以轉發的 args 為參數調用存儲的任務。任務傳回值或任何抛出的異常被存儲于 *this 的共享狀态。
僅在目前線程退出,并銷毀所有線程局域存儲期對象後,才令共享狀态就緒
代碼如下
#include <future>
#include <iostream>
#include <chrono>
#include <thread>
#include <functional>
#include <utility>
void worker(std::future<void>& output)
{
std::packaged_task<void(bool&)> my_task{ [](bool& done) { done=true; } };
auto result = my_task.get_future();
bool done = false;
//根據列印已經可以看到,此時已經執行了package_task的線程内容
//但是共享狀态到函數作用域結束之前并未就緒
my_task.make_ready_at_thread_exit(done); // 立即執行任務
std::cout << "worker: done = " << std::boolalpha << done << std::endl;
auto status = result.wait_for(std::chrono::seconds(0));
if (status == std::future_status::timeout)
std::cout << "worker: result is not ready yet" << std::endl;
output = std::move(result);
}
int main()
{
std::future<void> result;
std::thread{worker, std::ref(result)}.join();
//等到線程函數傳回結果,且future對象就緒之後,可以看到列印狀态變為就緒
auto status = result.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready)
std::cout << "main: result is ready" << std::endl;
}
總結
綜上對
package_task
的描述,我們可以看到package_task模版類就像
promise
一樣可以被異步調用,并且将調用對象執行的結果封裝在
future
的共享狀态中,來讓調用者擷取。同時
package_task
的調用者擷取調用對象的執行結果過程就像async的惰性求值政策,當調用者想要擷取調用對象的執行結果時才開始執行調用對象。