1. 程式人生 > 程式設計 >C++11中std::packaged_task的使用詳解

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://www.jb51.net/article/179229.htm

模板類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=/valid
 std::packaged_task<int(int)> foo; // default-constructed
 std::packaged_task<int(int)> bar([](int x) { return x * 2; }); // initialized
 
 foo = std::move(bar); // move-assignment
 std::cout << "valid: " << foo.valid() << "\n";
 std::future<int> ret = foo.get_future(); // get future
 std::thread(std::move(foo),10).detach(); // spawn thread and call task
 
 int value = ret.get(); // wait for the task to finish and get result
 std::cout << "The double of 10 is " << value << ".\n";
}
 
{ // reset/operator()
 std::packaged_task<int(int)> tsk([](int x) { return x * 3; }); // package task
 
 std::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_future
 auto 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_task
 std::future<int> ret = tsk.get_future(); // get future
 
 std::thread th(std::move(tsk),5,0); // spawn thread to count down from 5 to 0
 
 int value = ret.get(); // wait for the task to finish and get result
 std::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()
{
{ // lambda
 std::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';
}
 
{ // bind
 std::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';
}
 
{ // thread
 std::packaged_task<int(int,int)> task([](int x,y); });
 std::future<int> result = task.get_future();
 
 std::thread task_td(std::move(task),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 data
 std::string data = "Data From " + token;
 return data;
 }
};
 
int test_packaged_task_3()
{
 // Create a packaged_task<> that encapsulated a Function Object
 std::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 asynchronously
 std::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 1
 auto 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 it
 std::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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。