Translation from: https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/
In this section, we will discuss std::packaged_task
std::packaged_task<>
std::packaged_ Task < > is a class template that represents an asynchronous task. Encapsulated
1. Callable entities, i.e. functions, lambda functions or function objects
2. A shared state that stores returned values or thrown exceptions through associated callbacks.
Suppose we have an existing function that extracts data from the database and returns it,
//from DB get data std::string gtDataFromDB(std::string token){ //Do some stuff to fetch the data std::string data = "Data fetched from DB by filter :: + token; return data;
Now we want to execute the function in a separate thread, but what if we get the result or exception return in the main thread when other threads end?
One way is to change the function declaration and pass std::promise in the function. Before passing the std::promise < > object in the thread function, take out the relevant std::future < > from it and save it in the main thread. Now, before the thread function returns its value, it should be set in the passed std::promise parameter, so it can be used in the related std::future object of the main thread.
However, if we use STD:: packaged_ Task < >, you can avoid creating std::promise and changing function code.
Create STD:: packaged_ Task < > object
std::packaged_ The task < > object is a class template, so we need to pass the template parameters to packaged_ Task < >, which is the type of function to call.
//Create a that encapsulates the callback function packaged_task<> std::packaged_task<std::string(std::string)> task(getDataFromDB);
Get future object
//from packaged_task<>Get related future<> std::future<std::string> result = task.get_future();
Pass packaged_ Task < > to the thread
std::packaged_ Task < > can be moved but not copied, so we need to pass it to the thread
//transmit packaged_task<>Give threads asynchronous operation std::thread th(std::move(task), "Arg");
Because packaged_task can only be moved, not copied, so we took the STD:: future < > object from it before moving it to the thread.
The thread will execute this task, which internally calls the relevant callable entities, such as our function getDataFromDB().
When this function gives the return value, STD:: packaged_ Task < > set it to the associated shared state, and the results or exceptions returned by getDataFromDB() will eventually be available in the associated future objects.
//obtain packaged_task<>The result returned is getDataFromDB()The value returned. std::string data = result.get();
The get() function blocks the calling thread until a callable entity returns and STD:: packaged_ Task < > set the data to its shareable state
#include <iostream> #include <thread> #include <future> #include <string> //from DB get data std::string getDataFromDB(std::string token) { //get data std::string data = "Data fetched from DB by Filter :: " + token; return data; } int main() { //Create a that encapsulates the callback function packaged_task<> std::packaged_task<std::string(std::string)> task(getDataFromDB); //from packaged_task<>Get related future<> std::future<std::string> result = task.get_future(); //take packaged_task Pass to thread to run asynchronously std::thread th(std::move(task), "Arg"); //join Thread, blocking until the thread completes th.join(); //obtain packaged_task<>As a result, i.e getDataFromDB()Return value of std::string data = result.get(); std::cout << data << std::endl; return 0; }
Output:
Data fetched from DB by Filter :: Arg
We can create a packaged with a lambda function or function object on the same line_ task
#include <iostream> #include <thread> #include <future> #include <string> int main() { //Created encapsulation lambda Functional packaged_task<> std::packaged_task<std::string(std::string)> task([](std::string token) { std::string data = "Data From " + token; return data; }); //from packaged_task<>Get related future<> std::future<std::string> result = task.get_future(); //take packaged_task Pass to thread to run asynchronously std::thread th(std::move(task), "Arg"); //join Thread, blocking until the thread completes th.join(); //obtain packaged_task<>As a result, i.e getDataFromDB()Return value of std::string data = result.get(); std::cout << data << std::endl; return 0; }
Output:
Data fetched from DB by Filter :: Arg
Creating packaged using function objects_ task
#include <iostream> #include <thread> #include <future> #include <string> //from DB Function object to get data struct DBDataFetcher { std::string operator ()(std::string token) { std::string data = "Data From " + token; return data; } }; int main() { //Create using function objects packaged_task std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher())); //from packaged_task<>Get related future<> std::future<std::string> result = task.get_future(); //take packaged_task Pass to thread to run asynchronously std::thread th(std::move(task), "Arg"); //join Thread, blocking until the thread completes th.join(); //obtain packaged_task<>As a result, i.e getDataFromDB()Return value of std::string data = result.get(); std::cout << data << std::endl; return 0; }
Output:
Data fetched from DB by Filter :: Arg