Varun July 2, 2017 C++11 Multithreading – Part 10: packaged_task Example and Tutorial2018-08-18T15:23:07+00:00C++ 11, c++11 Threads, Multithreading, packaged_task 2 Comments
In this example we will discuss c++11 std::packaged_task feature and its uses.
std::packaged_task<> is a class template and represents a asynchronous task. It encapsulates,
Suppose we have an existing function that fetches the data from DB and return i.e.
1 2 3 4 5 6 7 |
// Fetch some data from DB std::string getDataFromDB( 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 this function in a separate thread. But how we will fetch the result or exception back in main thread after other thread is finished ?
One way is to change the declaration of function and pass a std::promise<> in the function. Before passing the std::promise<> object in thread function, fetch the associated std::future<> out of it and keep that in main thread. Now, before thread function returns its value, it should set that in passed std::promise<> argument, so that it can be available in associated std::future<> object in main thread. Check below tutorial for this approach i.e,
C++11 Multithreading – Part 8: std::future , std::promise and Returning values from Thread
But creating this std::promise<> and changing function code can be prevented if we use std::packaged_task<>.
std::packaged_task<> can wrap around a normal function and make it applicable to run as asynchronous function.
When std::packaged_task<> is called in a separate thread, it calls the associated callback and stores the return value/exception in its internal shared state. This value can be accessed in other thread or main function through std::future<> object.
Let’s create a std::packaged_task<> from above mentioned function, execute in separate thread and fetch result from its future<> object.
Creating std::packaged_task<> object
std::package_task<> is a class template, therefore we need to pass template parameter to packaged_task<> i.e. type of callable function
1 2 |
// Create a packaged_task<> that encapsulated the callback i.e. a function std::packaged_task |
Fetch the future object from it,
1 2 |
// Fetch the associated future<> from packaged_task<> std::future |
Passing packaged_task<> to a thread,
std::packaged_task<> is movable but not copy-able, so we need to move it to thread i.e.
1 2 |
// Pass the packaged_task to thread to run asynchronously std::thread th(std::move(task), "Arg"); |
As packaged_task was only movable and not copy-able, therefore we fetched the std::future<> object from it before moving it to thread.
Thread will execute this task, which internally calls associated callable entity i.e. our function getDataFromDB().
Now when this function returns the value, std::packaged_task<> sets it to associated shared state and result or exception returned by getDataFromDB() will eventually be available in associated future object.
In main function, fetch result from future<> object i.e.
1 2 |
// Fetch the result of packaged_task<> i.e. value returned by getDataFromDB() std::string data = result.get(); |
get() function will block the calling thread until the callable entity returns and std::packaged_task<> set the data in its shareable state.
Complete example is as follows,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
#include #include #include #include
// Fetch some data from DB std::string getDataFromDB( std::string token) { // Do some stuff to fetch the data std::string data = "Data fetched from DB by Filter :: " + token; return data; }
int main() {
// Create a packaged_task<> that encapsulated the callback i.e. a function std::packaged_task
// Fetch the associated future<> from packaged_task<> std::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; } |
Output:
1 |
Data fetched from DB by Filter :: Arg |
On similar lines we can create a packaged_task with lambda function and function objects too i.e.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include #include #include #include
int main() {
// Create a packaged_task<> that encapsulated a lambda function std::packaged_task // Do some stuff to fetch the data std::string data = "Data From " + token; return data; });
// Fetch the associated future<> from packaged_task<> std::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; } |
Output:
1 |
Data fetched from DB by Filter :: Arg |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
#include #include #include #include
/* * Function Object to Fetch Data from DB */ struct DBDataFetcher { std::string operator()(std::string token) { // Do some stuff to fetch the data std::string data = "Data From " + token; return data; } };
int main() {
// Create a packaged_task<> that encapsulated a lambda function std::packaged_task
// Fetch the associated future<> from packaged_task<> std::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; } |
Output:
1 |
Data fetched from DB by Filter :: Arg |
ps:
就packaged_task来说, 其能完成的, 通过future/promise都能完成. 但是对于已有代码来说, 使用packaged_task能有更少的浸入性, 不用为了异步执行函数修改现有代码, 也不用为了异步执行函数在封装一个函数传入promise调用已有函数.