Details of C ::packaged_task used in C++11

  • 2020-07-21 09:20:31
  • OfStack

std::packaged_task in C++11 is a template class. std::packaged_task wraps any callable target (function, lambda expression, bind expression, function object) so that it can be invoked asynchronously. Its return value or thrown exception is stored in a Shared state that can be accessed through the std::future object.

std::packaged_task is similar to std::function, but its result is automatically passed to the std::future object.

The std::packaged_task object contains two elements inside: (1). The task stored (stored task) is a collection of callable objects (such as function Pointers, members, or Pointers to function objects)(A stored task, which is callable object (such as function pointer, pointer to function object). (2). Shared state, which can store the result of calling the stored task (stored task), And can be accessed asynchronously via std::future (A shared state, which is able store of calling stored and be through a future).

The Shared state is associated with the std::future object by calling the ES78en_packaged_task member. After the call, the two objects share the same Shared state: (1). The std::packaged_task object is an asynchronous provider (asynchronous provider) and should be set to ready at some point by calling the stored task (stored task). (2).std::future object is an asynchronous return object that can retrieve the value of the Shared state and wait for it to be ready when necessary.

The Shared state has a lifetime of at least until the last object associated with it is released or destroyed.

std::packaged_task does not start itself, you have to call it (A packaged_task won't start it own, you have invoke it).

std: : future introduces reference: https: / / www ofstack. com article / 179229. htm

Template class std::packaged_task member functions include:

Constructor: (1). Default constructor: initializes the object in the case of no Shared state and no storage task (no shared state no stored task). (2). initialization constructor: This object has a Shared state and its stored tasks are initialized by fn. (3). initialization constructor with allocator. (4). Disable copy construction. (5) Mobile structure is supported.

Destructor :(1). Discard the (abandon) Shared state and destroy the packaged_task object. (2). If there are other future objects associated with the Shared state with 1, the Shared state itself will not be destroyed. (3). If the packaged_task object is destroyed before the Shared state is ready, the Shared state is automatically ready and contains an exception of type std::future_error.

get_future function: (1). Returns an std::future object associated with the Shared state of the packaged_task object. (2).1 Once the stored task is called, the returned std::future object can access the value or exception set on the Shared state by the packaged_task object. (3). Each packaged_task Shared state can only be retrieved by one std::future object (Only one object can be for for state). (4). After calling this function, packaged_task should have its Shared state ready at some point (by calling its stored task), or it will be automatically ready after destruction and contain an exception of type std::future_error.

make_ready_at_thread_exit function: share state ready only when the thread exits rather than immediately after the call is completed.

5. operator= (1). Disable copy assignment. (2). Mobile assignment is supported.

6. operator() :(1). (2). If the call to the storage task completes successfully or throws an exception, the returned value or caught exception is stored in the Shared state, which is ready (unblock all threads currently waiting for it).

reset function :(1). Resets the object with the new Shared state while keeping the same storage task. (2). Allow to invoke the stored task again. (3). The previously Shared state associated with the object is abandoned (just as packaged_task was destroyed). (4). Inside, This function behaves like an packaged_task1 (Internally, the function behaves as if ES220en-ES221en newly constructed packaged_ES232en) assigned to a new construct.

swap functions/non-member template functions swap: Tasks to exchange Shared state and storage (stored task).

valid function: Checks to see if the packaged_task object has a Shared state.

Please refer to the following test code for detailed usage. The following is the test code of copy from other articles, and some adjustments are made. For detailed introduction, please refer to the corresponding 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, 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 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


Related articles: