C++11中std::packaged_task的使用
C++11中的std::packaged_task是個模板類。std::packaged_task包裝任何可調用目標(函數、lambda表達式、bind表達式、函數對象)以便它可以被異步調用。它的返回值或拋出的異常被存儲于能通過std::future對象訪問的共享狀態中。
std::packaged_task類似于std::function,但是會自動將其結果傳遞給std::future對象。
std::packaged_task對象內部包含兩個元素:(1).存儲的任務(stored task)是一些可調用的對象(例如函數指針、成員或函數對象的指針)( A stored task, which is some callable object (such as a function pointer, pointer to member or function object))。(2).共享狀態,它可以存儲調用存儲的任務(stored task)的結果,并可以通過std::future進行異步訪問(A shared state, which is able to store the results of calling the stored task and be accessed asynchronously through a future)。
通過調用std::packaged_task的get_future成員將共享狀態與std::future對象關聯。調用之后,兩個對象共享相同的共享狀態:(1).std::packaged_task對象是異步提供程序(asynchronous provider),應通過調用存儲的任務(stored task)在某個時刻將共享狀態設置為就緒。(2).std::future對象是一個異步返回對象,可以檢索共享狀態的值,并在必要時等待其準備就緒。
共享狀態的生存期至少要持續到與之關聯的最后一個對象釋放或銷毀為止。
std::packaged_task不會自己啟動,你必須調用它(A packaged_task won't start on it's own, you have to invoke it)。
std::future介紹參考:https://blog.csdn.net/fengbingchun/article/details/104115489
模板類std::packaged_task成員函數包括:
1. 構造函數:(1).默認構造函數:無共享狀態無存儲任務(no shared state and no stored task)情況下初始化對象。(2). initialization constructor:該對象具有共享狀態,且其存儲的任務由fn初始化。(3). initialization constructor with allocator。(4).禁用拷貝構造。(5).支持移動構造。
2. 析構函數:(1).放棄(abandon)共享狀態并銷毀packaged_task對象。(2). 如果有其它future對象關聯到同一共享狀態,則共享狀態本身不會被銷毀。(3). 如果packaged_task對象在共享狀態準備就緒前被銷毀,則共享狀態自動準備就緒并包含一個std::future_error類型的異常。
3. get_future函數:(1).返回一個與packaged_task對象的共享狀態關聯的std::future對象。(2).一旦存儲的任務被調用,返回的std::future對象就可以訪問packaged_task對象在共享狀態上設置的值或異常。(3).每個packaged_task共享狀態只能被一個std::future對象檢索(Only one future object can be retrieved for each packaged_task shared state)。(4).調用此函數后,packaged_task應在某個時候使其共享狀態準備就緒(通過調用其存儲的任務),否則將在銷毀后自動準備就緒并包含一個std::future_error類型的異常。
4. make_ready_at_thread_exit函數:在線程退出時才使共享狀態ready而不是在調用完成后就立即ready。
5. operator=:(1).禁用拷貝賦值。(2).支持移動賦值。
6. operator():(1).call stored task。(2).如果對存儲任務的調用成功完成或拋出異常,則返回的值或捕獲的異常存儲在共享狀態,共享狀態準備就緒(解除阻塞當前等待它的所有線程)。
7. reset函數:(1).在保持相同存儲的任務的同時,以新的共享狀態重置對象。(2).允許再次調用存儲的任務。(3).與對象關聯的之前的共享狀態被放棄(就像packaged_task被銷毀了一樣)。(4).在內部,該函數的行為就像是移動賦值了一個新構造的packaged_task一樣(Internally, the function behaves as if move-assigned a newly constructed packaged_task (with its stored task as argument))。
8. swap函數/非成員模板函數swap:交換共享狀態和存儲的任務(stored task)。
9. valid函數:檢查packaged_task對象是否具有共享狀態。
詳細用法見下面的測試代碼,下面是從其他文章中copy的測試代碼,部分作了調整,詳細內容介紹可以參考對應的reference:
#include "future.hpp"
#include <iostream>
#include <future>
#include <chrono>
#include <utility>
#include <thread>
#include <functional>
#include <memory>
#include <exception>
#include <numeric>
#include <vector>
#include <cmath>
#include <string>namespace future_ {///
// reference: http://www.cplusplus.com/reference/future/packaged_task/
int test_packaged_task_1()
{{ // constructor/get_future/operator=/validstd::packaged_task<int(int)> foo; // default-constructedstd::packaged_task<int(int)> bar([](int x) { return x * 2; }); // initializedfoo = std::move(bar); // move-assignmentstd::cout << "valid: " << foo.valid() << "\n";std::future<int> ret = foo.get_future(); // get futurestd::thread(std::move(foo), 10).detach(); // spawn thread and call taskint value = ret.get(); // wait for the task to finish and get resultstd::cout << "The double of 10 is " << value << ".\n";
}{ // reset/operator()std::packaged_task<int(int)> tsk([](int x) { return x * 3; }); // package taskstd::future<int> fut = tsk.get_future();tsk(33);std::cout << "The triple of 33 is " << fut.get() << ".\n";// re-use same task object:tsk.reset();fut = tsk.get_future();std::thread(std::move(tsk), 99).detach();std::cout << "Thre triple of 99 is " << fut.get() << ".\n";
}{ // constructor/get_futureauto countdown = [](int from, int to) {for (int i = from; i != to; --i) {std::cout << i << '\n';std::this_thread::sleep_for(std::chrono::seconds(1));}std::cout << "Lift off!\n";return from - to;};std::packaged_task<int(int, int)> tsk(countdown); // set up packaged_taskstd::future<int> ret = tsk.get_future(); // get futurestd::thread th(std::move(tsk), 5, 0); // spawn thread to count down from 5 to 0int value = ret.get(); // wait for the task to finish and get resultstd::cout << "The countdown lasted for " << value << " seconds.\n";th.join();
}return 0;
}///
// reference: https://en.cppreference.com/w/cpp/thread/packaged_task
int test_packaged_task_2()
{
{ // lambdastd::packaged_task<int(int, int)> task([](int a, int b) { return std::pow(a, b);});std::future<int> result = task.get_future();task(2, 9);std::cout << "task_lambda:\t" << result.get() << '\n';
}{ // bindstd::packaged_task<int()> task(std::bind([](int x, int y) { return std::pow(x, y); }, 2, 11));std::future<int> result = task.get_future();task();std::cout << "task_bind:\t" << result.get() << '\n';
}{ // threadstd::packaged_task<int(int, int)> task([](int x, int y) { return std::pow(x, y); });std::future<int> result = task.get_future();std::thread task_td(std::move(task), 2, 10);task_td.join();std::cout << "task_thread:\t" << result.get() << '\n';
}return 0;
}///
// reference: https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/
struct DBDataFetcher {std::string operator()(std::string token){// Do some stuff to fetch the datastd::string data = "Data From " + token;return data;}
};int test_packaged_task_3()
{// Create a packaged_task<> that encapsulated a Function Objectstd::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher()));// Fetch the associated future<> from packaged_task<>std::future<std::string> result = task.get_future();// Pass the packaged_task to thread to run asynchronouslystd::thread th(std::move(task), "Arg");// Join the thread. Its blocking and returns when thread is finished.th.join();// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB()std::string data = result.get();std::cout << data << std::endl;return 0;
}///
// reference: https://stackoverflow.com/questions/18143661/what-is-the-difference-between-packaged-task-and-async
int test_packaged_task_4()
{// sleeps for one second and returns 1auto sleep = []() {std::this_thread::sleep_for(std::chrono::seconds(1));return 1;};{ // std::packaged_task// >>>>> A packaged_task won't start on it's own, you have to invoke itstd::packaged_task<int()> task(sleep);auto f = task.get_future();task(); // invoke the function// You have to wait until task returns. Since task calls sleep// you will have to wait at least 1 second.std::cout << "You can see this after 1 second\n";// However, f.get() will be available, since task has already finished.std::cout << f.get() << std::endl;
}{ // std::async// >>>>> On the other hand, std::async with launch::async will try to run the task in a different thread :auto f = std::async(std::launch::async, sleep);std::cout << "You can see this immediately!\n";// However, the value of the future will be available after sleep has finished// so f.get() can block up to 1 second.std::cout << f.get() << "This will be shown after a second!\n";
}return 0;
}} // namespace future_
GitHub:https://github.com/fengbingchun/Messy_Test
總結
以上是生活随笔為你收集整理的C++11中std::packaged_task的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++11中std::shared_fu
- 下一篇: C++11中std::async的使用